diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/bfd.c | 19 | ||||
| -rw-r--r-- | lib/bfd.h | 4 | ||||
| -rw-r--r-- | lib/command.h | 1 | ||||
| -rw-r--r-- | lib/event.c | 22 | ||||
| -rw-r--r-- | lib/frrevent.h | 8 | ||||
| -rw-r--r-- | lib/mgmt_be_client.c | 44 | ||||
| -rw-r--r-- | lib/nexthop.c | 87 | ||||
| -rw-r--r-- | lib/nexthop.h | 104 | ||||
| -rw-r--r-- | lib/northbound.c | 28 | ||||
| -rw-r--r-- | lib/northbound.h | 3 | ||||
| -rw-r--r-- | lib/northbound_notif.c | 149 | ||||
| -rw-r--r-- | lib/privs.c | 9 | ||||
| -rwxr-xr-x | lib/route_types.pl | 15 | ||||
| -rw-r--r-- | lib/routemap.h | 1 | ||||
| -rw-r--r-- | lib/routemap_cli.c | 4 | ||||
| -rw-r--r-- | lib/srcdest_table.c | 10 | ||||
| -rw-r--r-- | lib/srcdest_table.h | 2 | ||||
| -rw-r--r-- | lib/srv6.h | 38 | ||||
| -rw-r--r-- | lib/yang.c | 54 | ||||
| -rw-r--r-- | lib/yang.h | 19 | ||||
| -rw-r--r-- | lib/zclient.c | 199 | ||||
| -rw-r--r-- | lib/zclient.h | 27 |
22 files changed, 611 insertions, 236 deletions
@@ -18,6 +18,7 @@ #include "table.h" #include "vty.h" #include "bfd.h" +#include "bfdd/bfd.h" DEFINE_MTYPE_STATIC(LIB, BFD_INFO, "BFD info"); DEFINE_MTYPE_STATIC(LIB, BFD_SOURCE, "BFD source cache"); @@ -140,14 +141,15 @@ static void bfd_source_cache_put(struct bfd_session_params *session); * bfd_get_peer_info - Extract the Peer information for which the BFD session * went down from the message sent from Zebra to clients. */ -static struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp, - struct prefix *sp, int *status, - int *remote_cbit, vrf_id_t vrf_id) +static struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp, struct prefix *sp, + int *status, int *remote_cbit, vrf_id_t vrf_id, + char *bfd_name) { unsigned int ifindex; struct interface *ifp = NULL; int plen; int local_remote_cbit; + uint8_t bfd_name_len = 0; /* * If the ifindex lookup fails the @@ -194,6 +196,13 @@ static struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp, STREAM_GETC(s, local_remote_cbit); if (remote_cbit) *remote_cbit = local_remote_cbit; + + STREAM_GETC(s, bfd_name_len); + if (bfd_name_len) { + STREAM_GET(bfd_name, s, bfd_name_len); + *(bfd_name + bfd_name_len) = 0; + } + return ifp; stream_failure: @@ -918,6 +927,7 @@ int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS) struct prefix dp; struct prefix sp; char ifstr[128], cbitstr[32]; + char bfd_name[BFD_NAME_SIZE + 1] = { 0 }; if (!zclient->bfd_integration) return 0; @@ -926,8 +936,7 @@ int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS) if (bsglobal.shutting_down) return 0; - ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &state, &remote_cbit, - vrf_id); + ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &state, &remote_cbit, vrf_id, bfd_name); /* * When interface lookup fails or an invalid stream is read, we must * not proceed otherwise it will trigger an assertion while checking @@ -26,6 +26,8 @@ extern "C" { #define BFD_PROFILE_NAME_LEN 64 +#define BFD_NAME_SIZE 255 + const char *bfd_get_status_str(int status); extern void bfd_client_sendmsg(struct zclient *zclient, int command, @@ -409,6 +411,8 @@ struct bfd_session_arg { uint32_t min_tx; /** Detection multiplier. */ uint32_t detection_multiplier; + /* bfd session name*/ + char bfd_name[BFD_NAME_SIZE + 1]; }; /** diff --git a/lib/command.h b/lib/command.h index c60751789f..dfd732893b 100644 --- a/lib/command.h +++ b/lib/command.h @@ -154,6 +154,7 @@ enum node_type { PCEP_PCE_NODE, /* PCE configuration node */ PCEP_PCC_NODE, /* PCC configuration node */ SRV6_NODE, /* SRv6 node */ + SRV6_SIDS_NODE, /* SRv6 SIDs node */ SRV6_LOCS_NODE, /* SRv6 locators node */ SRV6_LOC_NODE, /* SRv6 locator node */ SRV6_ENCAP_NODE, /* SRv6 encapsulation node */ diff --git a/lib/event.c b/lib/event.c index d95b3021a7..6081ba4727 100644 --- a/lib/event.c +++ b/lib/event.c @@ -111,6 +111,11 @@ static struct cpu_event_history *cpu_records_get(struct event_loop *loop, return res; } +static void cpu_records_clear(struct cpu_event_history *p) +{ + memset(p->_clear_begin, 0, p->_clear_end - p->_clear_begin); +} + static void cpu_records_free(struct cpu_event_history **p) { XFREE(MTYPE_EVENT_STATS, *p); @@ -250,20 +255,15 @@ static void cpu_record_clear(uint8_t filter) for (ALL_LIST_ELEMENTS_RO(masters, ln, m)) { frr_with_mutex (&m->mtx) { struct cpu_event_history *item; - struct cpu_records_head old[1]; - cpu_records_init(old); - cpu_records_swap_all(old, m->cpu_records); - - while ((item = cpu_records_pop(old))) { + /* it isn't possible to free the memory here + * because some of these will be in use (e.g. + * the one we're currently running in!) + */ + frr_each (cpu_records, m->cpu_records, item) { if (item->types & filter) - cpu_records_free(&item); - else - cpu_records_add(m->cpu_records, - item); + cpu_records_clear(item); } - - cpu_records_fini(old); } } } diff --git a/lib/frrevent.h b/lib/frrevent.h index 44776b29a7..c35b39a147 100644 --- a/lib/frrevent.h +++ b/lib/frrevent.h @@ -139,6 +139,10 @@ struct cpu_event_history { struct cpu_records_item item; void (*func)(struct event *e); + + /* fields between the pair of these two are nulled on "clear event cpu" */ + char _clear_begin[0]; + atomic_size_t total_cpu_warn; atomic_size_t total_wall_warn; atomic_size_t total_starv_warn; @@ -149,6 +153,10 @@ struct cpu_event_history { } real; struct time_stats cpu; atomic_uint_fast32_t types; + + /* end of cleared region */ + char _clear_end[0]; + const char *funcname; }; diff --git a/lib/mgmt_be_client.c b/lib/mgmt_be_client.c index 3a07a1d2d9..806242ed53 100644 --- a/lib/mgmt_be_client.c +++ b/lib/mgmt_be_client.c @@ -1114,19 +1114,24 @@ static void be_client_handle_notify(struct mgmt_be_client *client, void *msgbuf, size_t msg_len) { struct mgmt_msg_notify_data *notif_msg = msgbuf; - struct nb_node *nb_node; - struct lyd_node *dnode; + struct nb_node *nb_node, *nb_parent; + struct lyd_node *dnode = NULL; const char *data = NULL; const char *notif; - LY_ERR err; + bool is_yang_notify; + LY_ERR err = LY_SUCCESS; debug_be_client("Received notification for client %s", client->name); notif = mgmt_msg_native_xpath_data_decode(notif_msg, msg_len, data); - if (!notif || !data) { + if (!notif) { log_err_be_client("Corrupt notify msg"); return; } + if (!data && (notif_msg->op == NOTIFY_OP_DS_REPLACE || notif_msg->op == NOTIFY_OP_DS_PATCH)) { + log_err_be_client("Corrupt replace/patch notify msg: missing data"); + return; + } nb_node = nb_node_find(notif); if (!nb_node) { @@ -1134,20 +1139,41 @@ static void be_client_handle_notify(struct mgmt_be_client *client, void *msgbuf, return; } - if (!nb_node->cbs.notify) { + is_yang_notify = !!CHECK_FLAG(nb_node->snode->nodetype, LYS_NOTIF); + + if (is_yang_notify && !nb_node->cbs.notify) { debug_be_client("No notification callback for: %s", notif); return; } - err = yang_parse_notification(notif, notif_msg->result_type, data, + if (!nb_node->cbs.notify) { + /* + * See if a parent has a callback, this is so backend's can + * listen for changes on an entire datastore sub-tree. + */ + for (nb_parent = nb_node->parent; nb_parent; nb_parent = nb_node->parent) + if (nb_parent->cbs.notify) + break; + if (!nb_parent) { + debug_be_client("Including parents, no DS notification callback for: %s", + notif); + return; + } + nb_node = nb_parent; + } + + if (data && is_yang_notify) { + err = yang_parse_notification(notif, notif_msg->result_type, data, &dnode); + } else if (data) { + err = yang_parse_data(notif, notif_msg->result_type, false, true, false, data, &dnode); + } if (err) { - log_err_be_client("Can't parse notification data for: %s", - notif); + log_err_be_client("Can't parse notification data for: %s", notif); return; } - nb_callback_notify(nb_node, notif, dnode); + nb_callback_notify(nb_node, notif_msg->op, notif, dnode); lyd_free_all(dnode); } diff --git a/lib/nexthop.c b/lib/nexthop.c index 332581fbd8..ee6c2b7ec0 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -772,68 +772,30 @@ unsigned int nexthop_level(const struct nexthop *nexthop) return rv; } -/* Only hash word-sized things, let cmp do the rest. */ -uint32_t nexthop_hash_quick(const struct nexthop *nexthop) +uint32_t nexthop_hash(const struct nexthop *nexthop) { uint32_t key = 0x45afe398; - int i; - key = jhash_3words(nexthop->type, nexthop->vrf_id, - nexthop->nh_label_type, key); - - if (nexthop->nh_label) { - int labels = nexthop->nh_label->num_labels; + /* type, vrf, ifindex, ip addresses - see nexthop.h */ + key = _nexthop_hash_bytes(nexthop, key); - i = 0; + key = jhash_1word(nexthop->flags & NEXTHOP_FLAGS_HASHED, key); - while (labels >= 3) { - key = jhash_3words(nexthop->nh_label->label[i], - nexthop->nh_label->label[i + 1], - nexthop->nh_label->label[i + 2], - key); - labels -= 3; - i += 3; - } - - if (labels >= 2) { - key = jhash_2words(nexthop->nh_label->label[i], - nexthop->nh_label->label[i + 1], - key); - labels -= 2; - i += 2; - } + if (nexthop->nh_label) { + const struct mpls_label_stack *ls = nexthop->nh_label; - if (labels >= 1) - key = jhash_1word(nexthop->nh_label->label[i], key); + /* num_labels itself isn't useful to hash, if the number of + * labels is different, the hash value will change just due to + * that already. + */ + key = jhash(ls->label, sizeof(ls->label[0]) * ls->num_labels, key); } - key = jhash_2words(nexthop->ifindex, - CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK), - key); - /* Include backup nexthops, if present */ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { int backups = nexthop->backup_num; - i = 0; - - while (backups >= 3) { - key = jhash_3words(nexthop->backup_idx[i], - nexthop->backup_idx[i + 1], - nexthop->backup_idx[i + 2], key); - backups -= 3; - i += 3; - } - - while (backups >= 2) { - key = jhash_2words(nexthop->backup_idx[i], - nexthop->backup_idx[i + 1], key); - backups -= 2; - i += 2; - } - - if (backups >= 1) - key = jhash_1word(nexthop->backup_idx[i], key); + key = jhash(nexthop->backup_idx, sizeof(nexthop->backup_idx[0]) * backups, key); } if (nexthop->nh_srv6) { @@ -868,31 +830,6 @@ uint32_t nexthop_hash_quick(const struct nexthop *nexthop) return key; } - -#define GATE_SIZE 4 /* Number of uint32_t words in struct g_addr */ - -/* For a more granular hash */ -uint32_t nexthop_hash(const struct nexthop *nexthop) -{ - uint32_t gate_src_rmap_raw[GATE_SIZE * 3] = {}; - /* Get all the quick stuff */ - uint32_t key = nexthop_hash_quick(nexthop); - - assert(((sizeof(nexthop->gate) + sizeof(nexthop->src) - + sizeof(nexthop->rmap_src)) - / 3) - == (GATE_SIZE * sizeof(uint32_t))); - - memcpy(gate_src_rmap_raw, &nexthop->gate, GATE_SIZE); - memcpy(gate_src_rmap_raw + GATE_SIZE, &nexthop->src, GATE_SIZE); - memcpy(gate_src_rmap_raw + (2 * GATE_SIZE), &nexthop->rmap_src, - GATE_SIZE); - - key = jhash2(gate_src_rmap_raw, (GATE_SIZE * 3), key); - - return key; -} - void nexthop_copy_no_recurse(struct nexthop *copy, const struct nexthop *nexthop, struct nexthop *rparent) diff --git a/lib/nexthop.h b/lib/nexthop.h index 5dfb58d846..cea7c77e3e 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -8,6 +8,7 @@ #ifndef _LIB_NEXTHOP_H #define _LIB_NEXTHOP_H +#include "jhash.h" #include "prefix.h" #include "mpls.h" #include "vxlan.h" @@ -56,15 +57,48 @@ struct nexthop { struct nexthop *next; struct nexthop *prev; - /* - * What vrf is this nexthop associated with? + + /* begin of hashed data - all fields from here onwards are given to + * jhash() as one consecutive chunk. DO NOT create "padding holes". + * DO NOT insert pointers that need to be deep-hashed. + * + * static_assert() below needs to be updated when fields are added */ + char _hash_begin[0]; + + /* see above */ + enum nexthop_types_t type; + + /* What vrf is this nexthop associated with? */ vrf_id_t vrf_id; /* Interface index. */ ifindex_t ifindex; - enum nexthop_types_t type; + /* Type of label(s), if any */ + enum lsp_types_t nh_label_type; + + /* padding: keep 16 byte alignment here */ + + /* Nexthop address + * make sure all 16 bytes for IPv6 are zeroed when putting in an IPv4 + * address since the entire thing is hashed as-is + */ + union { + union g_addr gate; + enum blackhole_type bh_type; + }; + union g_addr src; + union g_addr rmap_src; /* Src is set via routemap */ + + /* end of hashed data - remaining fields in this struct are not + * directly fed into jhash(). Most of them are actually part of the + * hash but have special rules or handling attached. + */ + char _hash_end[0]; + + /* Weight of the nexthop ( for unequal cost ECMP ) */ + uint8_t weight; uint16_t flags; #define NEXTHOP_FLAG_ACTIVE (1 << 0) /* This nexthop is alive. */ @@ -82,18 +116,15 @@ struct nexthop { #define NEXTHOP_FLAG_EVPN (1 << 8) /* nexthop is EVPN */ #define NEXTHOP_FLAG_LINKDOWN (1 << 9) /* is not removed on link down */ + /* which flags are part of nexthop_hash(). Should probably be split + * off into a separate field... + */ +#define NEXTHOP_FLAGS_HASHED NEXTHOP_FLAG_ONLINK + #define NEXTHOP_IS_ACTIVE(flags) \ (CHECK_FLAG(flags, NEXTHOP_FLAG_ACTIVE) \ && !CHECK_FLAG(flags, NEXTHOP_FLAG_DUPLICATE)) - /* Nexthop address */ - union { - union g_addr gate; - enum blackhole_type bh_type; - }; - union g_addr src; - union g_addr rmap_src; /* Src is set via routemap */ - /* Nexthops obtained by recursive resolution. * * If the nexthop struct needs to be resolved recursively, @@ -104,15 +135,9 @@ struct nexthop { /* Recursive parent */ struct nexthop *rparent; - /* Type of label(s), if any */ - enum lsp_types_t nh_label_type; - /* Label(s) associated with this nexthop. */ struct mpls_label_stack *nh_label; - /* Weight of the nexthop ( for unequal cost ECMP ) */ - uint8_t weight; - /* Count and index of corresponding backup nexthop(s) in a backup list; * only meaningful if the HAS_BACKUP flag is set. */ @@ -138,6 +163,29 @@ struct nexthop { struct nexthop_srv6 *nh_srv6; }; +/* all hashed fields (including padding, if it is necessary to add) need to + * be listed in the static_assert below + */ + +#define S(field) sizeof(((struct nexthop *)NULL)->field) + +static_assert( + offsetof(struct nexthop, _hash_end) - offsetof(struct nexthop, _hash_begin) == + S(type) + S(vrf_id) + S(ifindex) + S(nh_label_type) + S(gate) + S(src) + S(rmap_src), + "struct nexthop contains padding, this can break things. insert _pad fields at appropriate places"); + +#undef S + +/* this is here to show exactly what is meant by the comments above about + * the hashing + */ +static inline uint32_t _nexthop_hash_bytes(const struct nexthop *nh, uint32_t seed) +{ + return jhash(&nh->_hash_begin, + offsetof(struct nexthop, _hash_end) - offsetof(struct nexthop, _hash_begin), + seed); +} + /* Utility to append one nexthop to another. */ #define NEXTHOP_APPEND(to, new) \ do { \ @@ -183,27 +231,11 @@ struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type, /* * Hash a nexthop. Suitable for use with hash tables. * - * This function uses the following values when computing the hash: - * - vrf_id - * - ifindex - * - type - * - gate - * - * nexthop - * The nexthop to hash - * - * Returns: - * 32-bit hash of nexthop + * Please double check the code on what is included in the hash, there was + * documentation here but it got outdated and the only thing worse than no + * doc is incorrect doc. */ uint32_t nexthop_hash(const struct nexthop *nexthop); -/* - * Hash a nexthop only on word-sized attributes: - * - vrf_id - * - ifindex - * - type - * - (some) flags - */ -uint32_t nexthop_hash_quick(const struct nexthop *nexthop); extern bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2); extern bool nexthop_same_no_labels(const struct nexthop *nh1, diff --git a/lib/northbound.c b/lib/northbound.c index 418cb246f6..60794b8728 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -685,19 +685,30 @@ void nb_config_diff(const struct nb_config *config1, lyd_free_all(diff); } -static int dnode_create(struct nb_config *candidate, const char *xpath, - const char *value, uint32_t options, - struct lyd_node **new_dnode) +/** + * dnode_create() - create a new node in the tree + * @candidate: config tree to create node in. + * @xpath: target node to create. + * @value: value for the new if required. + * @options: lyd_new_path options + * @new_dnode: the newly created node. If options includes LYD_NEW_PATH_UPDATE, + * and the node exists (i.e., isn't create but updated), then + * new_node will be set to NULL not the existing node). + * + * Return: NB_OK or NB_ERR. + */ +static LY_ERR dnode_create(struct nb_config *candidate, const char *xpath, const char *value, + uint32_t options, struct lyd_node **new_dnode) { struct lyd_node *dnode; LY_ERR err; - err = lyd_new_path(candidate->dnode, ly_native_ctx, xpath, value, - options, &dnode); + err = lyd_new_path2(candidate->dnode, ly_native_ctx, xpath, value, 0, 0, options, NULL, + &dnode); if (err) { flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path(%s) failed: %d", __func__, xpath, err); - return NB_ERR; + return err; } else if (dnode) { err = lyd_new_implicit_tree(dnode, LYD_IMPLICIT_NO_STATE, NULL); if (err) { @@ -708,7 +719,7 @@ static int dnode_create(struct nb_config *candidate, const char *xpath, } if (new_dnode) *new_dnode = dnode; - return NB_OK; + return LY_SUCCESS; } int nb_candidate_edit(struct nb_config *candidate, const struct nb_node *nb_node, @@ -1857,7 +1868,7 @@ int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath, return nb_node->cbs.rpc(&args); } -void nb_callback_notify(const struct nb_node *nb_node, const char *xpath, +void nb_callback_notify(const struct nb_node *nb_node, uint8_t op, const char *xpath, struct lyd_node *dnode) { struct nb_cb_notify_args args = {}; @@ -1865,6 +1876,7 @@ void nb_callback_notify(const struct nb_node *nb_node, const char *xpath, DEBUGD(&nb_dbg_cbs_notify, "northbound notify: %s", xpath); args.xpath = xpath; + args.op = op; args.dnode = dnode; nb_node->cbs.notify(&args); } diff --git a/lib/northbound.h b/lib/northbound.h index ce59bfd01a..c31f007e70 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -305,6 +305,7 @@ struct nb_cb_rpc_args { struct nb_cb_notify_args { /* XPath of the notification. */ const char *xpath; + uint8_t op; /* * libyang data node representing the notification. If the notification @@ -861,7 +862,7 @@ extern const void *nb_callback_lookup_next(const struct nb_node *nb_node, extern int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath, const struct lyd_node *input, struct lyd_node *output, char *errmsg, size_t errmsg_len); -extern void nb_callback_notify(const struct nb_node *nb_node, const char *xpath, +extern void nb_callback_notify(const struct nb_node *nb_node, uint8_t op, const char *xpath, struct lyd_node *dnode); /* diff --git a/lib/northbound_notif.c b/lib/northbound_notif.c index b75c865613..9caca9f6d7 100644 --- a/lib/northbound_notif.c +++ b/lib/northbound_notif.c @@ -480,87 +480,96 @@ static struct op_changes_group *op_changes_group_next(void) /* Query for changes and notify */ /* ---------------------------- */ +static void timer_walk_abort(struct nb_notif_walk_args *args); static void timer_walk_continue(struct event *event); +static void timer_walk_done(struct nb_notif_walk_args *args); + +static struct op_change *__next_change(struct op_changes_group *group) +{ + struct op_change *next = RB_NEXT(op_changes, group->cur_change); + + /* Remove and free current so retry works */ + RB_REMOVE(op_changes, group->cur_changes, group->cur_change); + op_change_free(group->cur_change); + return next; +} + +static struct op_changes_group *__next_group(struct op_changes_group *group) +{ + __dbg("done with oper-path collection for group"); + op_changes_group_free(group); + return op_changes_group_next(); +} static enum nb_error oper_walk_done(const struct lyd_node *tree, void *arg, enum nb_error ret) { struct nb_notif_walk_args *args = arg; struct op_changes_group *group = args->group; const char *path = group->cur_change->path; - const char *op = group->cur_changes == &group->adds ? "add" : "delete"; /* we don't send batches when yielding as we need completed edit in any patch */ assert(ret != NB_YIELD); - nb_notif_walk = NULL; - if (ret == NB_ERR_NOT_FOUND) { __dbg("Path not found while walking oper tree: %s", path); - XFREE(MTYPE_NB_NOTIF_WALK_ARGS, args); - return ret; - } - /* Something else went wrong with the walk */ - if (ret != NB_OK) { + ret = NB_OK; + } else if (ret != NB_OK) { error: - __log_err("Error notifying for datastore change on path: %s: %s", path, - nb_err_name(ret)); - XFREE(MTYPE_NB_NOTIF_WALK_ARGS, args); - /* XXX Need to inform mgmtd/front-ends things are out-of-sync */ - return ret; - } - - __dbg("done with oper-path collection for %s path: %s", op, path); - - /* Do we need this? */ - while (tree->parent) - tree = lyd_parent(tree); - - /* Send the add (replace) notification */ - if (mgmt_be_send_ds_replace_notification(path, tree)) { - ret = NB_ERR; - goto error; + __log_err("Error notifying for datastore path: %s: %s", path, nb_err_name(ret)); + + timer_walk_abort(args); + goto done; + } else { + __dbg("Done with oper-path collection for path: %s", path); + + /* Do we need this? */ + while (tree->parent) + tree = lyd_parent(tree); + + /* Send the add (replace) notification */ + if (mgmt_be_send_ds_replace_notification(path, tree)) { + __log_err("Error sending notification message for path: %s", path); + ret = NB_ERR; + goto error; + } } /* - * Advance to next change (either dels or adds or both). + * Advance to next change. */ - group->cur_change = RB_NEXT(op_changes, group->cur_change); + group->cur_change = __next_change(group); if (!group->cur_change) { - __dbg("done with oper-path collection for group"); - op_changes_group_free(group); - - group = op_changes_group_next(); - args->group = group; - if (!group) { - __dbg("done with ALL oper-path collection for notification"); - XFREE(MTYPE_NB_NOTIF_WALK_ARGS, args); + args->group = __next_group(group); + if (!args->group) { + timer_walk_done(args); goto done; } } + /* Run next walk after giving other events a shot to run */ event_add_timer_msec(nb_notif_master, timer_walk_continue, args, 0, &nb_notif_timer); done: /* Done with current walk and scheduled next one if there is more */ nb_notif_walk = NULL; - return NB_OK; + return ret; } -static LY_ERR nb_notify_delete_changes(struct nb_notif_walk_args *args) +static int nb_notify_delete_changes(struct nb_notif_walk_args *args) { struct op_changes_group *group = args->group; - LY_ERR err; group->cur_change = RB_MIN(op_changes, group->cur_changes); while (group->cur_change) { - err = mgmt_be_send_ds_delete_notification(group->cur_change->path); - assert(err == LY_SUCCESS); /* XXX */ - - group->cur_change = RB_NEXT(op_changes, group->cur_change); + if (mgmt_be_send_ds_delete_notification(group->cur_change->path)) { + __log_err("Error sending delete notification message for path: %s", + group->cur_change->path); + return 1; + } + group->cur_change = __next_change(group); } - - return LY_SUCCESS; + return 0; } static void timer_walk_continue(struct event *event) @@ -568,15 +577,17 @@ static void timer_walk_continue(struct event *event) struct nb_notif_walk_args *args = EVENT_ARG(event); struct op_changes_group *group = args->group; const char *path; - LY_ERR err; + int ret; /* * Notify about deletes until we have add changes to collect. */ while (group->cur_changes == &group->dels) { - err = nb_notify_delete_changes(args); - assert(err == LY_SUCCESS); /* XXX */ - assert(!group->cur_change); /* we send all the deletes in one message */ + ret = nb_notify_delete_changes(args); + if (ret) { + timer_walk_abort(args); + return; + } /* after deletes advance to adds */ group->cur_changes = &group->adds; @@ -584,16 +595,12 @@ static void timer_walk_continue(struct event *event) if (group->cur_change) break; - __dbg("done with oper-path change group"); - op_changes_group_free(group); - - group = op_changes_group_next(); - args->group = group; - if (!group) { - __dbg("done with ALL oper-path changes"); - XFREE(MTYPE_NB_NOTIF_WALK_ARGS, args); + args->group = __next_group(group); + if (!args->group) { + timer_walk_done(args); return; } + group = args->group; } path = group->cur_change->path; @@ -621,6 +628,22 @@ static void timer_walk_start(struct event *event) timer_walk_continue(event); } +static void timer_walk_abort(struct nb_notif_walk_args *args) +{ + __dbg("Failed notifying datastore changes, will retry"); + + __dbg("oper-state notify setting retry timer to fire in: %d msec ", NB_NOTIF_TIMER_MSEC); + event_add_timer_msec(nb_notif_master, timer_walk_continue, args, NB_NOTIF_TIMER_MSEC, + &nb_notif_timer); +} + +static void timer_walk_done(struct nb_notif_walk_args *args) +{ + __dbg("Finished notifying for all datastore changes"); + assert(!args->group); + XFREE(MTYPE_NB_NOTIF_WALK_ARGS, args); +} + static void nb_notif_set_walk_timer(void) { if (nb_notif_walk) { @@ -659,19 +682,23 @@ void nb_notif_init(struct event_loop *tm) void nb_notif_terminate(void) { - struct nb_notif_walk_args *args; + struct nb_notif_walk_args *args = nb_notif_timer ? EVENT_ARG(nb_notif_timer) : NULL; struct op_changes_group *group; + __dbg("terminating: timer: %p timer arg: %p walk %p", nb_notif_timer, args, nb_notif_walk); + EVENT_OFF(nb_notif_timer); if (nb_notif_walk) { - nb_oper_cancel_walk(nb_notif_walk); - /* need to free the group that's in the walk */ + /* Grab walk args from walk if active. */ args = nb_oper_walk_finish_arg(nb_notif_walk); - if (args) - op_changes_group_free(args->group); + nb_oper_cancel_walk(nb_notif_walk); nb_notif_walk = NULL; } + if (args) { + op_changes_group_free(args->group); + XFREE(MTYPE_NB_NOTIF_WALK_ARGS, args); + } while ((group = op_changes_group_next())) op_changes_group_free(group); diff --git a/lib/privs.c b/lib/privs.c index b0809bf690..e7df383e5d 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -210,10 +210,11 @@ int zprivs_change_caps(zebra_privs_ops_t op) { cap_flag_value_t cflag; - /* should be no possibility of being called without valid caps */ - assert(zprivs_state.syscaps_p && zprivs_state.caps); - if (!(zprivs_state.syscaps_p && zprivs_state.caps)) - exit(1); + /* Called without valid caps - just return. Not every daemon needs + * privs. + */ + if (zprivs_state.syscaps_p == NULL || zprivs_state.caps == NULL) + return 0; if (op == ZPRIVS_RAISE) cflag = CAP_SET; diff --git a/lib/route_types.pl b/lib/route_types.pl index c75a866964..834cb822d2 100755 --- a/lib/route_types.pl +++ b/lib/route_types.pl @@ -127,9 +127,12 @@ printf "#define SHOW_ROUTE_V6_HEADER \\\n%s\n", codelist(@protosv6); print "\n"; sub collect { - my ($daemon, $ipv4, $ipv6, $any) = @_; + my ($daemon, $ipv4, $ipv6, $any, $ip_prot) = @_; my (@names, @help) = ((), ()); for my $p (@protos) { + next if ($ip_prot == 1 && $daemon eq "zebra" && $protodetail{$p}->{"cname"} eq "kernel"); + next if ($ip_prot == 1 && $daemon eq "zebra" && $protodetail{$p}->{"cname"} eq "connected"); + next if ($ip_prot == 1 && $daemon eq "zebra" && $protodetail{$p}->{"cname"} eq "local"); next if ($protodetail{$p}->{"daemon"} eq $daemon && $daemon ne "zebra"); next if ($protodetail{$p}->{"restrict2"} ne "" && $protodetail{$p}->{"restrict2"} ne $daemon); @@ -151,24 +154,24 @@ for my $daemon (sort keys %daemons) { next unless ($daemons{$daemon}->{"ipv4"} || $daemons{$daemon}->{"ipv6"}); printf "/* %s */\n", $daemon; if ($daemons{$daemon}->{"ipv4"} && $daemons{$daemon}->{"ipv6"}) { - my ($names, $help) = collect($daemon, 1, 1, 0); + my ($names, $help) = collect($daemon, 1, 1, 0, 0); printf "#define FRR_REDIST_STR_%s \\\n %s\n", uc $daemon, $names; printf "#define FRR_REDIST_HELP_STR_%s \\\n%s\n", uc $daemon, $help; - ($names, $help) = collect($daemon, 1, 0, 0); + ($names, $help) = collect($daemon, 1, 0, 0, 0); printf "#define FRR_IP_REDIST_STR_%s \\\n %s\n", uc $daemon, $names; printf "#define FRR_IP_REDIST_HELP_STR_%s \\\n%s\n", uc $daemon, $help; - ($names, $help) = collect($daemon, 0, 1, 0); + ($names, $help) = collect($daemon, 0, 1, 0, 0); printf "#define FRR_IP6_REDIST_STR_%s \\\n %s\n", uc $daemon, $names; printf "#define FRR_IP6_REDIST_HELP_STR_%s \\\n%s\n", uc $daemon, $help; if ($daemon eq "zebra") { - ($names, $help) = collect($daemon, 1, 0, 1); + ($names, $help) = collect($daemon, 1, 0, 1, 1); printf "#define FRR_IP_PROTOCOL_MAP_STR_%s \\\n %s\n", uc $daemon, $names; printf "#define FRR_IP_PROTOCOL_MAP_HELP_STR_%s \\\n%s\n", uc $daemon, $help; - ($names, $help) = collect($daemon, 0, 1, 1); + ($names, $help) = collect($daemon, 0, 1, 1, 1); printf "#define FRR_IP6_PROTOCOL_MAP_STR_%s \\\n %s\n", uc $daemon, $names; printf "#define FRR_IP6_PROTOCOL_MAP_HELP_STR_%s \\\n%s\n", uc $daemon, $help; } diff --git a/lib/routemap.h b/lib/routemap.h index 8dcc17ecc3..1c02348313 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -310,6 +310,7 @@ DECLARE_QOBJ_TYPE(route_map); (strmatch(C, "frr-bgp-route-map:ip-route-source")) #define IS_MATCH_ROUTE_SRC_PL(C) \ (strmatch(C, "frr-bgp-route-map:ip-route-source-prefix-list")) +#define IS_MATCH_COMMUNITY_LIMIT(C) (strmatch(C, "frr-bgp-route-map:match-community-limit")) #define IS_MATCH_COMMUNITY(C) \ (strmatch(C, "frr-bgp-route-map:match-community")) #define IS_MATCH_LCOMMUNITY(C) \ diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index 69b942064b..eb01709707 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -810,6 +810,10 @@ void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode, yang_dnode_get_string( dnode, "./rmap-match-condition/frr-bgp-route-map:list-name")); + } else if (IS_MATCH_COMMUNITY_LIMIT(condition)) { + vty_out(vty, " match community-limit %s\n", + yang_dnode_get_string(dnode, + "./rmap-match-condition/frr-bgp-route-map:community-limit")); } else if (IS_MATCH_COMMUNITY(condition)) { vty_out(vty, " match community %s", yang_dnode_get_string( diff --git a/lib/srcdest_table.c b/lib/srcdest_table.c index 3247a0372c..7203c8ac8e 100644 --- a/lib/srcdest_table.c +++ b/lib/srcdest_table.c @@ -309,13 +309,3 @@ static ssize_t printfrr_rn(struct fbuf *buf, struct printfrr_eargs *ea, cbuf, sizeof(cbuf)); return bputs(buf, cbuf); } - -struct route_table *srcdest_srcnode_table(struct route_node *rn) -{ - if (rnode_is_dstnode(rn)) { - struct srcdest_rnode *srn = srcdest_rnode_from_rnode(rn); - - return srn->src_table; - } - return NULL; -} diff --git a/lib/srcdest_table.h b/lib/srcdest_table.h index ff97f9b735..a699d4a11b 100644 --- a/lib/srcdest_table.h +++ b/lib/srcdest_table.h @@ -87,8 +87,6 @@ static inline void *srcdest_rnode_table_info(struct route_node *rn) return route_table_get_info(srcdest_rnode_table(rn)); } -extern struct route_table *srcdest_srcnode_table(struct route_node *rn); - #ifdef __cplusplus } #endif diff --git a/lib/srv6.h b/lib/srv6.h index 9a041e3d85..7e4fb97ad1 100644 --- a/lib/srv6.h +++ b/lib/srv6.h @@ -22,6 +22,8 @@ #define SRV6_SID_FORMAT_NAME_SIZE 512 +#define DEFAULT_SRV6_IFNAME "sr0" + #ifdef __cplusplus extern "C" { #endif @@ -186,6 +188,42 @@ enum srv6_endpoint_behavior_codepoint { SRV6_ENDPOINT_BEHAVIOR_OPAQUE = 0xFFFF, }; +/* + * Convert SRv6 endpoint behavior codepoints to human-friendly string. + */ +static inline const char * +srv6_endpoint_behavior_codepoint2str(enum srv6_endpoint_behavior_codepoint behavior) +{ + switch (behavior) { + case SRV6_ENDPOINT_BEHAVIOR_RESERVED: + return "Reserved"; + case SRV6_ENDPOINT_BEHAVIOR_END: + return "End"; + case SRV6_ENDPOINT_BEHAVIOR_END_X: + return "End.X"; + case SRV6_ENDPOINT_BEHAVIOR_END_DT6: + return "End.DT6"; + case SRV6_ENDPOINT_BEHAVIOR_END_DT4: + return "End.DT4"; + case SRV6_ENDPOINT_BEHAVIOR_END_DT46: + return "End.DT46"; + case SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID: + return "uN"; + case SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID: + return "uA"; + case SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID: + return "uDT6"; + case SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID: + return "uDT4"; + case SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID: + return "uDT46"; + case SRV6_ENDPOINT_BEHAVIOR_OPAQUE: + return "Opaque"; + } + + return "Unspec"; +} + struct nexthop_srv6 { /* SRv6 localsid info for Endpoint-behaviour */ enum seg6local_action_t seg6local_action; diff --git a/lib/yang.c b/lib/yang.c index 2aa3539259..dd48d8861b 100644 --- a/lib/yang.c +++ b/lib/yang.c @@ -874,6 +874,60 @@ static void ly_zlog_cb(LY_LOG_LEVEL level, const char *msg, const char *data_pat zlog(priority, "libyang: %s", msg); } +LY_ERR yang_parse_data(const char *xpath, LYD_FORMAT format, bool as_subtree, bool is_oper, + bool validate, const char *data, struct lyd_node **tree) +{ + struct ly_in *in = NULL; + struct lyd_node *subtree = NULL; + uint32_t parse_options = LYD_PARSE_STRICT | LYD_PARSE_ONLY; + uint32_t validate_options = LYD_VALIDATE_PRESENT; + LY_ERR err; + + err = ly_in_new_memory(data, &in); + if (err != LY_SUCCESS) + return err; + + if (as_subtree) { + struct lyd_node *parent; + + /* + * Create the subtree branch from root using the xpath. This + * will be used below to parse the data rooted at the subtree -- + * a common YANG JSON technique (vs XML which starts all + * data trees from the root). + */ + err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, 0, &parent, &subtree); + if (err != LY_SUCCESS) + goto done; + err = lyd_find_path(parent, xpath, false, &subtree); + if (err != LY_SUCCESS) + goto done; + } + + if (is_oper) + validate_options |= LYD_VALIDATE_OPERATIONAL; + +#ifdef LYD_VALIDATE_NOT_FINAL + if (!validate) + validate_options |= LYD_VALIDATE_NOT_FINAL; +#endif + + err = lyd_parse_data(ly_native_ctx, subtree, in, format, parse_options, validate_options, + tree); + if (err == LY_SUCCESS && subtree) + *tree = subtree; +done: + ly_in_free(in, 0); + if (err != LY_SUCCESS) { + if (*tree) + lyd_free_all(*tree); + else if (subtree) + lyd_free_all(subtree); + *tree = NULL; + } + return err; +} + LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format, const char *data, struct lyd_node **notif) { diff --git a/lib/yang.h b/lib/yang.h index eed2fa8dbe..748f089037 100644 --- a/lib/yang.h +++ b/lib/yang.h @@ -681,6 +681,25 @@ extern struct ly_ctx *yang_ctx_new_setup(bool embedded_modules, bool explicit_co */ extern void yang_debugging_set(bool enable); + +/* + * Parse YANG data. + * + * Args: + * xpath: xpath of the data. + * format: LYD_FORMAT of input data. + * as_subtree: parse the data as starting at the subtree identified by xpath. + * is_oper: parse as operational state allows for invalid (logs warning). + * validate: validate the data (otherwise treat as non-final). + * data: input data. + * notif: pointer to the libyang data tree to store the parsed notification. + * If the notification is not on the top level of the yang model, + * the pointer to the notification node is still returned, but it's + * part of the full data tree with all its parents. + */ +LY_ERR yang_parse_data(const char *xpath, LYD_FORMAT format, bool as_subtree, bool is_oper, + bool validate, const char *data, struct lyd_node **tree); + /* * Parse a YANG notification. * diff --git a/lib/zclient.c b/lib/zclient.c index 063944fd3b..5deea8f0cf 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -31,6 +31,7 @@ DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient"); DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs"); +DEFINE_MTYPE_STATIC(LIB, REDIST_TABLE_DIRECT, "Redistribution table direct"); /* Zebra client events. */ enum zclient_event { ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT }; @@ -104,6 +105,11 @@ void zclient_free(struct zclient *zclient) XFREE(MTYPE_ZCLIENT, zclient); } +static void redist_free_instance(void *data) +{ + XFREE(MTYPE_REDIST_INST, data); +} + unsigned short *redist_check_instance(struct redist_proto *red, unsigned short instance) { @@ -126,8 +132,10 @@ void redist_add_instance(struct redist_proto *red, unsigned short instance) red->enabled = 1; - if (!red->instances) + if (!red->instances) { red->instances = list_new(); + red->instances->del = redist_free_instance; + } in = XMALLOC(MTYPE_REDIST_INST, sizeof(unsigned short)); *in = instance; @@ -143,23 +151,100 @@ void redist_del_instance(struct redist_proto *red, unsigned short instance) return; listnode_delete(red->instances, id); - XFREE(MTYPE_REDIST_INST, id); + red->instances->del(id); if (!red->instances->count) { red->enabled = 0; list_delete(&red->instances); } } -void redist_del_all_instances(struct redist_proto *red) +static void redist_free_table_direct(void *data) { - struct listnode *ln, *nn; - unsigned short *id; + XFREE(MTYPE_REDIST_TABLE_DIRECT, data); +} + +struct redist_table_direct *redist_lookup_table_direct(const struct redist_proto *red, + const struct redist_table_direct *table) +{ + struct redist_table_direct *ntable; + struct listnode *node; + + if (red->instances == NULL) + return NULL; + + for (ALL_LIST_ELEMENTS_RO(red->instances, node, ntable)) { + if (table->vrf_id != ntable->vrf_id) + continue; + if (table->table_id != ntable->table_id) + continue; + + return ntable; + } + + return NULL; +} + +bool redist_table_direct_has_id(const struct redist_proto *red, int table_id) +{ + struct redist_table_direct *table; + struct listnode *node; + + if (red->instances == NULL) + return false; + for (ALL_LIST_ELEMENTS_RO(red->instances, node, table)) { + if (table->table_id != table_id) + continue; + + return true; + } + + return false; +} + +void redist_add_table_direct(struct redist_proto *red, const struct redist_table_direct *table) +{ + struct redist_table_direct *ntable; + + ntable = redist_lookup_table_direct(red, table); + if (ntable != NULL) + return; + + if (red->instances == NULL) { + red->instances = list_new(); + red->instances->del = redist_free_table_direct; + } + + red->enabled = 1; + + ntable = XCALLOC(MTYPE_REDIST_TABLE_DIRECT, sizeof(*ntable)); + ntable->vrf_id = table->vrf_id; + ntable->table_id = table->table_id; + listnode_add(red->instances, ntable); +} + +void redist_del_table_direct(struct redist_proto *red, const struct redist_table_direct *table) +{ + struct redist_table_direct *ntable; + + ntable = redist_lookup_table_direct(red, table); + if (ntable == NULL) + return; + + listnode_delete(red->instances, ntable); + red->instances->del(ntable); + if (red->instances->count == 0) { + red->enabled = 0; + list_delete(&red->instances); + } +} + +void redist_del_all_instances(struct redist_proto *red) +{ if (!red->instances) return; - for (ALL_LIST_ELEMENTS(red->instances, ln, nn, id)) - redist_del_instance(red, *id); + list_delete(&red->instances); } /* Stop zebra client services. */ @@ -480,6 +565,17 @@ enum zclient_send_status zclient_send_localsid(struct zclient *zclient, return zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); } +static void zclient_send_table_direct(struct zclient *zclient, afi_t afi, int type) +{ + struct redist_table_direct *table; + struct redist_proto *red = &zclient->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT]; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(red->instances, node, table)) + zebra_redistribute_send(type, zclient, afi, ZEBRA_ROUTE_TABLE_DIRECT, + table->table_id, table->vrf_id); +} + /* Send register requests to zebra daemon for the information in a VRF. */ void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id) { @@ -513,6 +609,12 @@ void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id) if (!zclient->mi_redist[afi][i].enabled) continue; + if (i == ZEBRA_ROUTE_TABLE_DIRECT) { + zclient_send_table_direct(zclient, afi, + ZEBRA_REDISTRIBUTE_ADD); + continue; + } + struct listnode *node; unsigned short *id; @@ -580,6 +682,12 @@ void zclient_send_dereg_requests(struct zclient *zclient, vrf_id_t vrf_id) if (!zclient->mi_redist[afi][i].enabled) continue; + if (i == ZEBRA_ROUTE_TABLE_DIRECT) { + zclient_send_table_direct(zclient, afi, + ZEBRA_REDISTRIBUTE_DELETE); + continue; + } + struct listnode *node; unsigned short *id; @@ -2016,6 +2124,15 @@ bool zapi_route_notify_decode(struct stream *s, struct prefix *p, enum zapi_route_notify_owner *note, afi_t *afi, safi_t *safi) { + struct prefix dummy; + + return zapi_route_notify_decode_srcdest(s, p, &dummy, tableid, note, afi, safi); +} + +bool zapi_route_notify_decode_srcdest(struct stream *s, struct prefix *p, struct prefix *src_p, + uint32_t *tableid, enum zapi_route_notify_owner *note, + afi_t *afi, safi_t *safi) +{ uint32_t t; afi_t afi_val; safi_t safi_val; @@ -2025,6 +2142,9 @@ bool zapi_route_notify_decode(struct stream *s, struct prefix *p, STREAM_GETC(s, p->family); STREAM_GETC(s, p->prefixlen); STREAM_GET(&p->u.prefix, s, prefix_blen(p)); + src_p->family = p->family; + STREAM_GETC(s, src_p->prefixlen); + STREAM_GET(&src_p->u.prefix, s, prefix_blen(src_p)); STREAM_GETL(s, t); STREAM_GETC(s, afi_val); STREAM_GETC(s, safi_val); @@ -2180,7 +2300,27 @@ struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh) n->type = znh->type; n->vrf_id = znh->vrf_id; n->ifindex = znh->ifindex; - n->gate = znh->gate; + + /* only copy values that have meaning - make sure "spare bytes" are + * left zeroed for hashing (look at _nexthop_hash_bytes) + */ + switch (znh->type) { + case NEXTHOP_TYPE_BLACKHOLE: + n->bh_type = znh->bh_type; + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + n->gate.ipv4 = znh->gate.ipv4; + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + n->gate.ipv6 = znh->gate.ipv6; + break; + case NEXTHOP_TYPE_IFINDEX: + /* nothing, ifindex is always copied */ + break; + } + n->srte_color = znh->srte_color; n->weight = znh->weight; @@ -4634,9 +4774,52 @@ static void zclient_read(struct event *thread) zclient_event(ZCLIENT_READ, zclient); } +static void zclient_redistribute_table_direct(struct zclient *zclient, vrf_id_t vrf_id, afi_t afi, + int instance, int command) +{ + struct redist_proto *red = &zclient->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT]; + bool has_table; + struct redist_table_direct table = { + .vrf_id = vrf_id, + .table_id = instance, + }; + + has_table = redist_lookup_table_direct(red, &table); + + if (command == ZEBRA_REDISTRIBUTE_ADD) { + if (has_table) + return; + + redist_add_table_direct(red, &table); + } else { + if (!has_table) + return; + + redist_del_table_direct(red, &table); + } + + if (zclient->sock > 0) + zebra_redistribute_send(command, zclient, afi, ZEBRA_ROUTE_TABLE_DIRECT, instance, + vrf_id); +} + void zclient_redistribute(int command, struct zclient *zclient, afi_t afi, int type, unsigned short instance, vrf_id_t vrf_id) { + /* + * When asking for table-direct redistribution the parameter + * `instance` has a different meaning: it means table + * identification. + * + * The table identification information is stored in + * `zclient->mi_redist` along with the VRF identification + * information in a pair (different from the usual single protocol + * instance value). + */ + if (type == ZEBRA_ROUTE_TABLE_DIRECT) { + zclient_redistribute_table_direct(zclient, vrf_id, afi, instance, command); + return; + } if (instance) { if (command == ZEBRA_REDISTRIBUTE_ADD) { diff --git a/lib/zclient.h b/lib/zclient.h index 2385a8a219..afd84acce2 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -268,6 +268,21 @@ struct redist_proto { struct list *instances; }; +/** + * Redistribute table direct instance data structure: keeps the VRF + * that subscribed to the table ID. + * + * **NOTE** + * `table_id` is an integer because that is what the netlink interface + * uses for route attribute RTA_TABLE (32bit int), however the whole + * zclient API uses `unsigned short` (and CLI commands) so it will be + * limited to the range 1 to 65535. + */ +struct redist_table_direct { + vrf_id_t vrf_id; + int table_id; +}; + struct zclient_capabilities { uint32_t ecmp; bool mpls_enabled; @@ -924,6 +939,15 @@ extern void redist_add_instance(struct redist_proto *, unsigned short); extern void redist_del_instance(struct redist_proto *, unsigned short); extern void redist_del_all_instances(struct redist_proto *red); +extern struct redist_table_direct * +redist_lookup_table_direct(const struct redist_proto *red, const struct redist_table_direct *table); +extern bool redist_table_direct_has_id(const struct redist_proto *red, int table_id); +extern void redist_add_table_direct(struct redist_proto *red, + const struct redist_table_direct *table); +extern void redist_del_table_direct(struct redist_proto *red, + const struct redist_table_direct *table); + + /* * Send to zebra that the specified vrf is using label to resolve * itself for L3VPN's. Repeated calls of this function with @@ -1144,6 +1168,9 @@ bool zapi_route_notify_decode(struct stream *s, struct prefix *p, uint32_t *tableid, enum zapi_route_notify_owner *note, afi_t *afi, safi_t *safi); +bool zapi_route_notify_decode_srcdest(struct stream *s, struct prefix *p, struct prefix *src_p, + uint32_t *tableid, enum zapi_route_notify_owner *note, + afi_t *afi, safi_t *safi); bool zapi_rule_notify_decode(struct stream *s, uint32_t *seqno, uint32_t *priority, uint32_t *unique, char *ifname, enum zapi_rule_notify_owner *note); |
