diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-08-05 09:56:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-08-05 09:56:24 +0000 |
commit | 54de8bbe36d7d968c6367939277942518bd4e11f (patch) | |
tree | 924f8c16d9c8831679f355b88c620b9b356c9d5a /lib | |
parent | Adding debian version 10.0.1-0.1. (diff) | |
download | frr-54de8bbe36d7d968c6367939277942518bd4e11f.tar.xz frr-54de8bbe36d7d968c6367939277942518bd4e11f.zip |
Merging upstream version 10.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib')
79 files changed, 4402 insertions, 2902 deletions
diff --git a/lib/affinitymap.c b/lib/affinitymap.c index e53d54a..6ff8e83 100644 --- a/lib/affinitymap.c +++ b/lib/affinitymap.c @@ -41,8 +41,6 @@ #include "jhash.h" DEFINE_MTYPE_STATIC(LIB, AFFINITY_MAP, "Affinity map"); -DEFINE_MTYPE(LIB, AFFINITY_MAP_NAME, "Affinity map name"); -DEFINE_MTYPE_STATIC(LIB, AFFINITY_MAP_INDEX, "Affinity map index"); DEFINE_QOBJ_TYPE(affinity_maps); DEFINE_QOBJ_TYPE(affinity_map); diff --git a/lib/agentx.c b/lib/agentx.c index 70ee675..19f2a6b 100644 --- a/lib/agentx.c +++ b/lib/agentx.c @@ -22,12 +22,13 @@ #include "hook.h" #include "libfrr.h" #include "xref.h" +#include "lib/libagentx.h" XREF_SETUP(); DEFINE_HOOK(agentx_enabled, (), ()); -static bool agentx_enabled = false; +//bool agentx_enabled = false; static struct event_loop *agentx_tm; static struct event *timeout_thr = NULL; @@ -153,15 +154,6 @@ static void agentx_events_update(void) netsnmp_large_fd_set_cleanup(&lfds); } -/* AgentX node. */ -static int config_write_agentx(struct vty *vty); -static struct cmd_node agentx_node = { - .name = "smux", - .node = SMUX_NODE, - .prompt = "", - .config_write = config_write_agentx, -}; - /* Logging NetSNMP messages */ static int agentx_log_callback(int major, int minor, void *serverarg, void *clientarg) @@ -201,17 +193,7 @@ static int agentx_log_callback(int major, int minor, void *serverarg, return SNMP_ERR_NOERROR; } -static int config_write_agentx(struct vty *vty) -{ - if (agentx_enabled) - vty_out(vty, "agentx\n"); - return 1; -} - -DEFUN (agentx_enable, - agentx_enable_cmd, - "agentx", - "SNMP AgentX protocol settings\n") +static int agentx_cli_on(void) { if (!agentx_enabled) { init_snmp(FRR_SMUX_NAME); @@ -221,19 +203,14 @@ DEFUN (agentx_enable, hook_call(agentx_enabled); } - return CMD_SUCCESS; + return 1; } -DEFUN (no_agentx, - no_agentx_cmd, - "no agentx", - NO_STR - "SNMP AgentX protocol settings\n") +static int agentx_cli_off(void) { if (!agentx_enabled) - return CMD_SUCCESS; - vty_out(vty, "SNMP AgentX support cannot be disabled once enabled\n"); - return CMD_WARNING_CONFIG_FAILED; + return 1; + return 0; } static int smux_disable(void) @@ -252,6 +229,9 @@ void smux_init(struct event_loop *tm) { agentx_tm = tm; + hook_register(agentx_cli_enabled, agentx_cli_on); + hook_register(agentx_cli_disabled, agentx_cli_off); + netsnmp_enable_subagent(); snmp_disable_log(); snmp_enable_calllog(); @@ -259,10 +239,6 @@ void smux_init(struct event_loop *tm) agentx_log_callback, NULL); init_agent(FRR_SMUX_NAME); - install_node(&agentx_node); - install_element(CONFIG_NODE, &agentx_enable_cmd); - install_element(CONFIG_NODE, &no_agentx_cmd); - hook_register(frr_early_fini, smux_disable); } diff --git a/lib/atomlist.h b/lib/atomlist.h index 2b6a3a1..3eb498a 100644 --- a/lib/atomlist.h +++ b/lib/atomlist.h @@ -7,7 +7,9 @@ #define _FRR_ATOMLIST_H #include "typesafe.h" +#ifndef _TYPESAFE_EXPAND_MACROS #include "frratomic.h" +#endif /* _TYPESAFE_EXPAND_MACROS */ #ifdef __cplusplus extern "C" { diff --git a/lib/base64.c b/lib/base64.c index ee2e838..00dd35f 100644 --- a/lib/base64.c +++ b/lib/base64.c @@ -11,7 +11,6 @@ #include "base64.h" #include "compiler.h" -static const int CHARS_PER_LINE = 72; static const char *ENCODING = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -19,7 +18,6 @@ void base64_init_encodestate(struct base64_encodestate *state_in) { state_in->step = step_A; state_in->result = 0; - state_in->stepcount = 0; } char base64_encode_value(char value_in) @@ -76,12 +74,6 @@ int base64_encode_block(const char *plaintext_in, int length_in, char *code_out, *codechar++ = base64_encode_value(result); result = (fragment & 0x03f) >> 0; *codechar++ = base64_encode_value(result); - - ++(state_in->stepcount); - if (state_in->stepcount == CHARS_PER_LINE/4) { - *codechar++ = '\n'; - state_in->stepcount = 0; - } } } /* control should not reach here */ @@ -105,7 +97,6 @@ int base64_encode_blockend(char *code_out, struct base64_encodestate *state_in) case step_A: break; } - *codechar++ = '\n'; return codechar - code_out; } diff --git a/lib/base64.h b/lib/base64.h index 839f92a..9bf4ace 100644 --- a/lib/base64.h +++ b/lib/base64.h @@ -14,7 +14,6 @@ enum base64_encodestep { struct base64_encodestate { enum base64_encodestep step; char result; - int stepcount; }; void base64_init_encodestate(struct base64_encodestate *state_in); diff --git a/lib/checksum.c b/lib/checksum.c index 6c5f06d..b1ad813 100644 --- a/lib/checksum.c +++ b/lib/checksum.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + /* * Checksum routine for Internet Protocol family headers (C Version). * diff --git a/lib/checksum.h b/lib/checksum.h index 508c3f3..2856a0d 100644 --- a/lib/checksum.h +++ b/lib/checksum.h @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + #ifndef _FRR_CHECKSUM_H #define _FRR_CHECKSUM_H diff --git a/lib/command.c b/lib/command.c index 8f780d0..51f2529 100644 --- a/lib/command.c +++ b/lib/command.c @@ -678,6 +678,21 @@ vector cmd_describe_command(vector vline, struct vty *vty, int *status) static struct list *varhandlers = NULL; +static int __add_key_comp(const struct lyd_node *dnode, void *arg) +{ + const char *key_value = yang_dnode_get_string(dnode, NULL); + + vector_set((vector)arg, XSTRDUP(MTYPE_COMPLETION, key_value)); + + return YANG_ITER_CONTINUE; +} + +static void __get_list_keys(vector comps, const char *xpath) +{ + yang_dnode_iterate(__add_key_comp, comps, + vty_shared_candidate_config->dnode, "%s", xpath); +} + void cmd_variable_complete(struct cmd_token *token, const char *arg, vector comps) { @@ -694,7 +709,10 @@ void cmd_variable_complete(struct cmd_token *token, const char *arg, if (cvh->varname && (!token->varname || strcmp(cvh->varname, token->varname))) continue; - cvh->completions(tmpcomps, token); + if (cvh->xpath) + __get_list_keys(tmpcomps, cvh->xpath); + if (cvh->completions) + cvh->completions(tmpcomps, token); break; } @@ -753,7 +771,7 @@ void cmd_variable_handler_register(const struct cmd_variable_handler *cvh) if (!varhandlers) return; - for (; cvh->completions; cvh++) + for (; cvh->completions || cvh->xpath; cvh++) listnode_add(varhandlers, (void *)cvh); } diff --git a/lib/command.h b/lib/command.h index 04c66ad..e4c575e 100644 --- a/lib/command.h +++ b/lib/command.h @@ -462,6 +462,8 @@ struct cmd_node { #define MPLS_LDP_SYNC_HOLDDOWN_STR \ "Time to wait for LDP-SYNC to occur before restoring if cost\n" #define NO_MPLS_LDP_SYNC_HOLDDOWN_STR "holddown timer disable\n" +#define BGP_AF_STR "Address Family\n" +#define BGP_AF_MODIFIER_STR "Address Family modifier\n" /* Command warnings. */ #define NO_PASSWD_CMD_WARNING \ @@ -636,6 +638,7 @@ extern void cmd_banner_motd_line(const char *line); struct cmd_variable_handler { const char *tokenname, *varname; + const char *xpath; /* fill comps from set of values at xpath */ void (*completions)(vector out, struct cmd_token *token); }; diff --git a/lib/compiler.h b/lib/compiler.h index 0326105..9d39026 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -455,6 +455,12 @@ _Static_assert(sizeof(_uint64_t) == 8 && sizeof(_int64_t) == 8, #define unlikely(_x) !!(_x) #endif +#ifdef __MACH__ +#define _DATA_SECTION(name) __attribute__((section("__DATA," name))) +#else +#define _DATA_SECTION(name) __attribute__((section(".data." name))) +#endif + #ifdef __cplusplus } #endif diff --git a/lib/elf_py.c b/lib/elf_py.c index 643495d..2b4fea3 100644 --- a/lib/elf_py.c +++ b/lib/elf_py.c @@ -1358,6 +1358,15 @@ bool elf_py_init(PyObject *pymod) (void)methods_elfpy; #endif +#if defined(HAVE_GELF_GETNOTE) && defined(HAVE_ELF_GETDATA_RAWCHUNK) + PyObject *elf_notes = Py_True; +#else + PyObject *elf_notes = Py_False; +#endif + Py_INCREF(elf_notes); + if (PyModule_AddObject(pymod, "elf_notes", elf_notes)) + Py_DECREF(elf_notes); + ELFFormatError = PyErr_NewException("_clippy.ELFFormatError", PyExc_ValueError, NULL); PyModule_AddObject(pymod, "ELFFormatError", ELFFormatError); diff --git a/lib/explicit_bzero.c b/lib/explicit_bzero.c index fa64ed8..e838f95 100644 --- a/lib/explicit_bzero.c +++ b/lib/explicit_bzero.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + /* * Public domain. * Written by Matthew Dempsky. diff --git a/lib/filter.c b/lib/filter.c index 5a0790f..0722bed 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -458,7 +458,6 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi, struct filter_cisco *filter; bool first; json_object *json = NULL; - json_object *json_proto = NULL; master = access_master_get(afi); if (master == NULL) { @@ -469,12 +468,7 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi, if (use_json) json = json_object_new_object(); - - /* Print the name of the protocol */ - if (json) { - json_proto = json_object_new_object(); - json_object_object_add(json, frr_protoname, json_proto); - } else + else vty_out(vty, "%s:\n", frr_protoname); for (access = master->str.head; access; access = access->next) { @@ -496,7 +490,7 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi, if (json) { json_acl = json_object_new_object(); - json_object_object_add(json_proto, + json_object_object_add(json, access->name, json_acl); @@ -596,7 +590,7 @@ DEFUN (show_mac_access_list_name, return filter_show(vty, argv[3]->arg, AFI_L2VPN, false); } -DEFUN (show_ip_access_list, +DEFUN_NOSH (show_ip_access_list, show_ip_access_list_cmd, "show ip access-list [json]", SHOW_STR @@ -608,7 +602,7 @@ DEFUN (show_ip_access_list, return filter_show(vty, NULL, AFI_IP, uj); } -DEFUN (show_ip_access_list_name, +DEFUN_NOSH (show_ip_access_list_name, show_ip_access_list_name_cmd, "show ip access-list ACCESSLIST4_NAME [json]", SHOW_STR @@ -622,7 +616,7 @@ DEFUN (show_ip_access_list_name, return filter_show(vty, argv[idx_acl]->arg, AFI_IP, uj); } -DEFUN (show_ipv6_access_list, +DEFUN_NOSH (show_ipv6_access_list, show_ipv6_access_list_cmd, "show ipv6 access-list [json]", SHOW_STR @@ -634,7 +628,7 @@ DEFUN (show_ipv6_access_list, return filter_show(vty, NULL, AFI_IP6, uj); } -DEFUN (show_ipv6_access_list_name, +DEFUN_NOSH (show_ipv6_access_list_name, show_ipv6_access_list_name_cmd, "show ipv6 access-list ACCESSLIST6_NAME [json]", SHOW_STR diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c index 1ffa593..3a4bc71 100644 --- a/lib/frr_pthread.c +++ b/lib/frr_pthread.c @@ -92,9 +92,14 @@ struct frr_pthread *frr_pthread_new(const struct frr_pthread_attr *attr, MTYPE_PTHREAD_PRIM, sizeof(pthread_mutex_t)); fpt->running_cond = XCALLOC(MTYPE_PTHREAD_PRIM, sizeof(pthread_cond_t)); + pthread_mutex_init(fpt->running_cond_mtx, NULL); pthread_cond_init(fpt->running_cond, NULL); + pthread_mutex_init(&fpt->startup_cond_mtx, NULL); + pthread_cond_init(&fpt->startup_cond, NULL); + fpt->started = false; + frr_with_mutex (&frr_pthread_list_mtx) { listnode_add(frr_pthread_list, fpt); } @@ -108,6 +113,8 @@ static void frr_pthread_destroy_nolock(struct frr_pthread *fpt) pthread_mutex_destroy(&fpt->mtx); pthread_mutex_destroy(fpt->running_cond_mtx); pthread_cond_destroy(fpt->running_cond); + pthread_mutex_destroy(&fpt->startup_cond_mtx); + pthread_cond_destroy(&fpt->startup_cond); XFREE(MTYPE_FRR_PTHREAD, fpt->name); XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond_mtx); XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond); @@ -140,11 +147,34 @@ int frr_pthread_set_name(struct frr_pthread *fpt) return ret; } +/* New pthread waits before running */ +static void frr_pthread_wait_startup(struct frr_pthread *fpt) +{ + frr_with_mutex (&fpt->startup_cond_mtx) { + while (!fpt->started) + pthread_cond_wait(&fpt->startup_cond, + &fpt->startup_cond_mtx); + } +} + +/* Parent pthread allows new pthread to start running */ +static void frr_pthread_notify_startup(struct frr_pthread *fpt) +{ + frr_with_mutex (&fpt->startup_cond_mtx) { + fpt->started = true; + pthread_cond_signal(&fpt->startup_cond); + } +} + static void *frr_pthread_inner(void *arg) { struct frr_pthread *fpt = arg; + /* The new pthead waits until the parent allows it to continue. */ + frr_pthread_wait_startup(fpt); + rcu_thread_start(fpt->rcu_thread); + return fpt->attr.start(fpt); } @@ -169,6 +199,9 @@ int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr) /* Restore caller's signals */ pthread_sigmask(SIG_SETMASK, &oldsigs, NULL); + /* Allow new child pthread to start */ + frr_pthread_notify_startup(fpt); + /* * Per pthread_create(3), the contents of fpt->thread are undefined if * pthread_create() did not succeed. Reset this value to zero. @@ -250,6 +283,8 @@ int frr_pthread_non_controlled_startup(pthread_t thread, const char *name, fpt->thread = thread; fpt->rcu_thread = rcu_thread; + fpt->started = true; + frr_pthread_inner(fpt); return 0; diff --git a/lib/frr_pthread.h b/lib/frr_pthread.h index 1e1b8d7..bb751b7 100644 --- a/lib/frr_pthread.h +++ b/lib/frr_pthread.h @@ -47,6 +47,17 @@ struct frr_pthread { struct frr_pthread_attr attr; /* + * Startup serialization: newly-started pthreads wait at a point + * very early in life so that there isn't a race with the + * starting pthread. The OS 'start' apis don't make any guarantees + * about which pthread runs first - the existing pthread that has + * called the 'start' api, or the new pthread that is just starting. + */ + pthread_cond_t startup_cond; + pthread_mutex_t startup_cond_mtx; + atomic_bool started; + + /* * Notification mechanism for allowing pthreads to notify their parents * when they are ready to do work. This mechanism has two associated * functions: diff --git a/lib/frr_zmq.c b/lib/frr_zmq.c index b28dd7f..5273d36 100644 --- a/lib/frr_zmq.c +++ b/lib/frr_zmq.c @@ -21,6 +21,8 @@ #include "log.h" #include "lib_errors.h" +XREF_SETUP(); + DEFINE_MTYPE_STATIC(LIB, ZEROMQ_CB, "ZeroMQ callback"); /* libzmq's context */ @@ -6,6 +6,12 @@ #include <zebra.h> +#include <net/if.h> + +#ifdef GNU_LINUX +#include <linux/if.h> +#endif /* GNU_LINUX */ + #include "linklist.h" #include "vector.h" #include "lib_errors.h" @@ -668,21 +674,26 @@ int if_is_running(const struct interface *ifp) if ptm checking is enabled, then ptm check has passed */ int if_is_operative(const struct interface *ifp) { - return ((ifp->flags & IFF_UP) - && (((ifp->flags & IFF_RUNNING) - && (ifp->ptm_status || !ifp->ptm_enable)) - || !CHECK_FLAG(ifp->status, - ZEBRA_INTERFACE_LINKDETECTION))); + return ((ifp->flags & IFF_UP) && + (((ifp->flags & IFF_RUNNING) +#ifdef IFF_LOWER_UP + && (ifp->flags & IFF_LOWER_UP) +#endif /* IFF_LOWER_UP */ + && (ifp->ptm_status || !ifp->ptm_enable)) || + !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION))); } /* Is the interface operative, eg. either UP & RUNNING or UP & !ZEBRA_INTERFACE_LINK_DETECTION, without PTM check */ int if_is_no_ptm_operative(const struct interface *ifp) { - return ((ifp->flags & IFF_UP) - && ((ifp->flags & IFF_RUNNING) - || !CHECK_FLAG(ifp->status, - ZEBRA_INTERFACE_LINKDETECTION))); + return ((ifp->flags & IFF_UP) && + (((ifp->flags & IFF_RUNNING) +#ifdef IFF_LOWER_UP + && (ifp->flags & IFF_LOWER_UP) +#endif /* IFF_LOWER_UP */ + ) || + !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION))); } /* Is this loopback interface ? */ @@ -744,6 +755,9 @@ const char *if_flag_dump(unsigned long flag) strlcpy(logbuf, "<", BUFSIZ); IFF_OUT_LOG(IFF_UP, "UP"); +#ifdef IFF_LOWER_UP + IFF_OUT_LOG(IFF_LOWER_UP, "LOWER_UP"); +#endif /* IFF_LOWER_UP */ IFF_OUT_LOG(IFF_BROADCAST, "BROADCAST"); IFF_OUT_LOG(IFF_DEBUG, "DEBUG"); IFF_OUT_LOG(IFF_LOOPBACK, "LOOPBACK"); @@ -885,21 +899,6 @@ nbr_connected_log(struct nbr_connected *connected, char *str) zlog_info("%s", logbuf); } -/* If two connected address has same prefix return 1. */ -static int connected_same_prefix(const struct prefix *p1, - const struct prefix *p2) -{ - if (p1->family == p2->family) { - if (p1->family == AF_INET - && IPV4_ADDR_SAME(&p1->u.prefix4, &p2->u.prefix4)) - return 1; - if (p1->family == AF_INET6 - && IPV6_ADDR_SAME(&p1->u.prefix6, &p2->u.prefix6)) - return 1; - } - return 0; -} - /* count the number of connected addresses that are in the given family */ unsigned int connected_count_by_family(struct interface *ifp, int family) { @@ -919,7 +918,7 @@ struct connected *connected_lookup_prefix_exact(struct interface *ifp, struct connected *ifc; frr_each (if_connected, ifp->connected, ifc) { - if (connected_same_prefix(ifc->address, p)) + if (prefix_same(ifc->address, p)) return ifc; } return NULL; @@ -932,7 +931,7 @@ struct connected *connected_delete_by_prefix(struct interface *ifp, /* In case of same prefix come, replace it with new one. */ frr_each_safe (if_connected, ifp->connected, ifc) { - if (connected_same_prefix(ifc->address, p)) { + if (prefix_same(ifc->address, p)) { if_connected_del(ifp->connected, ifc); return ifc; } diff --git a/lib/ipaddr.h b/lib/ipaddr.h index c86e38c..888955f 100644 --- a/lib/ipaddr.h +++ b/lib/ipaddr.h @@ -40,8 +40,9 @@ struct ipaddr { #define IS_IPADDR_V4(p) ((p)->ipa_type == IPADDR_V4) #define IS_IPADDR_V6(p) ((p)->ipa_type == IPADDR_V6) -#define SET_IPADDR_V4(p) (p)->ipa_type = IPADDR_V4 -#define SET_IPADDR_V6(p) (p)->ipa_type = IPADDR_V6 +#define SET_IPADDR_NONE(p) ((p)->ipa_type = IPADDR_NONE) +#define SET_IPADDR_V4(p) ((p)->ipa_type = IPADDR_V4) +#define SET_IPADDR_V6(p) ((p)->ipa_type = IPADDR_V6) #define IPADDRSZ(p) \ (IS_IPADDR_V4((p)) ? sizeof(struct in_addr) : sizeof(struct in6_addr)) @@ -165,9 +166,17 @@ static inline bool ipaddr_is_zero(const struct ipaddr *ip) return true; } +static inline bool ipaddr_is_same(const struct ipaddr *ip1, + const struct ipaddr *ip2) +{ + return ipaddr_cmp(ip1, ip2) == 0; +} + +/* clang-format off */ #ifdef _FRR_ATTRIBUTE_PRINTFRR #pragma FRR printfrr_ext "%pIA" (struct ipaddr *) #endif +/* clang-format on */ #ifdef __cplusplus } diff --git a/lib/keychain.c b/lib/keychain.c index 5ff0d1e..1982220 100644 --- a/lib/keychain.c +++ b/lib/keychain.c @@ -6,19 +6,19 @@ #include "config.h" #include <zebra.h> -#include "command.h" -#include "memory.h" -#include "linklist.h" #include "keychain.h" +#include "linklist.h" +#include "memory.h" -DEFINE_MTYPE_STATIC(LIB, KEY, "Key"); -DEFINE_MTYPE_STATIC(LIB, KEYCHAIN, "Key chain"); +DEFINE_MTYPE(LIB, KEY, "Key"); +DEFINE_MTYPE(LIB, KEYCHAIN, "Key chain"); +DEFINE_MTYPE(LIB, KEYCHAIN_DESC, "Key chain description"); DEFINE_QOBJ_TYPE(keychain); DEFINE_QOBJ_TYPE(key); /* Master list of key chain. */ -static struct list *keychain_list; +struct list *keychain_list; static struct keychain *keychain_new(void) { @@ -82,7 +82,7 @@ static void key_delete_func(struct key *key) key_free(key); } -static struct keychain *keychain_get(const char *name) +struct keychain *keychain_get(const char *name) { struct keychain *keychain; @@ -101,7 +101,7 @@ static struct keychain *keychain_get(const char *name) return keychain; } -static void keychain_delete(struct keychain *keychain) +void keychain_delete(struct keychain *keychain) { XFREE(MTYPE_KEYCHAIN, keychain->name); @@ -110,7 +110,7 @@ static void keychain_delete(struct keychain *keychain) keychain_free(keychain); } -static struct key *key_lookup(const struct keychain *keychain, uint32_t index) +struct key *key_lookup(const struct keychain *keychain, uint32_t index) { struct listnode *node; struct key *key; @@ -183,7 +183,7 @@ struct key *key_lookup_for_send(const struct keychain *keychain) return NULL; } -static struct key *key_get(const struct keychain *keychain, uint32_t index) +struct key *key_get(const struct keychain *keychain, uint32_t index) { struct key *key; @@ -200,7 +200,7 @@ static struct key *key_get(const struct keychain *keychain, uint32_t index) return key; } -static void key_delete(struct keychain *keychain, struct key *key) +void key_delete(struct keychain *keychain, struct key *key) { listnode_delete(keychain->key, key); @@ -208,122 +208,6 @@ static void key_delete(struct keychain *keychain, struct key *key) key_free(key); } -DEFUN_NOSH (key_chain, - key_chain_cmd, - "key chain WORD", - "Authentication key management\n" - "Key-chain management\n" - "Key-chain name\n") -{ - int idx_word = 2; - struct keychain *keychain; - - keychain = keychain_get(argv[idx_word]->arg); - VTY_PUSH_CONTEXT(KEYCHAIN_NODE, keychain); - - return CMD_SUCCESS; -} - -DEFUN (no_key_chain, - no_key_chain_cmd, - "no key chain WORD", - NO_STR - "Authentication key management\n" - "Key-chain management\n" - "Key-chain name\n") -{ - int idx_word = 3; - struct keychain *keychain; - - keychain = keychain_lookup(argv[idx_word]->arg); - - if (!keychain) { - vty_out(vty, "Can't find keychain %s\n", argv[idx_word]->arg); - return CMD_WARNING_CONFIG_FAILED; - } - - keychain_delete(keychain); - - return CMD_SUCCESS; -} - -DEFUN_NOSH (key, - key_cmd, - "key (0-2147483647)", - "Configure a key\n" - "Key identifier number\n") -{ - int idx_number = 1; - VTY_DECLVAR_CONTEXT(keychain, keychain); - struct key *key; - uint32_t index; - - index = strtoul(argv[idx_number]->arg, NULL, 10); - key = key_get(keychain, index); - VTY_PUSH_CONTEXT_SUB(KEYCHAIN_KEY_NODE, key); - - return CMD_SUCCESS; -} - -DEFUN (no_key, - no_key_cmd, - "no key (0-2147483647)", - NO_STR - "Delete a key\n" - "Key identifier number\n") -{ - int idx_number = 2; - VTY_DECLVAR_CONTEXT(keychain, keychain); - struct key *key; - uint32_t index; - - index = strtoul(argv[idx_number]->arg, NULL, 10); - key = key_lookup(keychain, index); - if (!key) { - vty_out(vty, "Can't find key %d\n", index); - return CMD_WARNING_CONFIG_FAILED; - } - - key_delete(keychain, key); - - vty->node = KEYCHAIN_NODE; - - return CMD_SUCCESS; -} - -DEFUN (key_string, - key_string_cmd, - "key-string LINE", - "Set key string\n" - "The key\n") -{ - int idx_line = 1; - VTY_DECLVAR_CONTEXT_SUB(key, key); - - if (key->string) - XFREE(MTYPE_KEY, key->string); - key->string = XSTRDUP(MTYPE_KEY, argv[idx_line]->arg); - - return CMD_SUCCESS; -} - -DEFUN (no_key_string, - no_key_string_cmd, - "no key-string [LINE]", - NO_STR - "Unset key string\n" - "The key\n") -{ - VTY_DECLVAR_CONTEXT_SUB(key, key); - - if (key->string) { - XFREE(MTYPE_KEY, key->string); - key->string = NULL; - } - - return CMD_SUCCESS; -} - const struct keychain_algo_info algo_info[] = { {KEYCHAIN_ALGO_NULL, "null", 0, 0, "NULL"}, {KEYCHAIN_ALGO_MD5, "md5", KEYCHAIN_MD5_HASH_SIZE, @@ -394,800 +278,6 @@ const char *keychain_get_algo_name_by_id(enum keychain_hash_algo key) return algo_info[key].name; } -DEFUN(cryptographic_algorithm, cryptographic_algorithm_cmd, - "cryptographic-algorithm " - "<md5|hmac-sha-1|hmac-sha-256|hmac-sha-384|hmac-sha-512>", - "Cryptographic-algorithm\n" - "Use MD5 algorithm\n" - "Use HMAC-SHA-1 algorithm\n" - "Use HMAC-SHA-256 algorithm\n" - "Use HMAC-SHA-384 algorithm\n" - "Use HMAC-SHA-512 algorithm\n") -{ - int algo_idx = 1; - uint8_t hash_algo = KEYCHAIN_ALGO_NULL; - - VTY_DECLVAR_CONTEXT_SUB(key, key); - hash_algo = keychain_get_algo_id_by_name(argv[algo_idx]->arg); -#ifndef CRYPTO_OPENSSL - if (hash_algo == KEYCHAIN_ALGO_NULL) { - vty_out(vty, - "Hash algorithm not supported, compile with --with-crypto=openssl\n"); - return CMD_WARNING_CONFIG_FAILED; - } -#endif /* CRYPTO_OPENSSL */ - key->hash_algo = hash_algo; - return CMD_SUCCESS; -} - -DEFUN(no_cryptographic_algorithm, no_cryptographic_algorithm_cmd, - "no cryptographic-algorithm " - "[<md5|hmac-sha-1|hmac-sha-256|hmac-sha-384|hmac-sha-512>]", - NO_STR - "Cryptographic-algorithm\n" - "Use MD5 algorithm\n" - "Use HMAC-SHA-1 algorithm\n" - "Use HMAC-SHA-256 algorithm\n" - "Use HMAC-SHA-384 algorithm\n" - "Use HMAC-SHA-512 algorithm\n") -{ - int algo_idx = 2; - uint8_t hash_algo = KEYCHAIN_ALGO_NULL; - - VTY_DECLVAR_CONTEXT_SUB(key, key); - if (argc > algo_idx) { - hash_algo = keychain_get_algo_id_by_name(argv[algo_idx]->arg); - if (hash_algo == KEYCHAIN_ALGO_NULL) { - vty_out(vty, - "Hash algorithm not supported, try compiling with --with-crypto=openssl\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } - - if ((hash_algo != KEYCHAIN_ALGO_NULL) && (hash_algo != key->hash_algo)) - return CMD_SUCCESS; - - key->hash_algo = KEYCHAIN_ALGO_NULL; - return CMD_SUCCESS; -} - -/* Convert HH:MM:SS MON DAY YEAR to time_t value. -1 is returned when - given string is malformed. */ -static time_t key_str2time(const char *time_str, const char *day_str, - const char *month_str, const char *year_str) -{ - int i = 0; - char *colon; - struct tm tm; - time_t time; - unsigned int sec, min, hour; - unsigned int day, month, year; - - const char *month_name[] = { - "January", "February", "March", "April", "May", - "June", "July", "August", "September", "October", - "November", "December", NULL}; - -#define _GET_LONG_RANGE(V, STR, MMCOND) \ - { \ - unsigned long tmpl; \ - char *endptr = NULL; \ - tmpl = strtoul((STR), &endptr, 10); \ - if (*endptr != '\0' || tmpl == ULONG_MAX) \ - return -1; \ - if (MMCOND) \ - return -1; \ - (V) = tmpl; \ - } -#define GET_LONG_RANGE(V, STR, MIN, MAX) \ - _GET_LONG_RANGE(V, STR, tmpl<(MIN) || tmpl>(MAX)) -#define GET_LONG_RANGE0(V, STR, MAX) _GET_LONG_RANGE(V, STR, tmpl > (MAX)) - - /* Check hour field of time_str. */ - colon = strchr(time_str, ':'); - if (colon == NULL) - return -1; - *colon = '\0'; - - /* Hour must be between 0 and 23. */ - GET_LONG_RANGE0(hour, time_str, 23); - - /* Check min field of time_str. */ - time_str = colon + 1; - colon = strchr(time_str, ':'); - if (*time_str == '\0' || colon == NULL) - return -1; - *colon = '\0'; - - /* Min must be between 0 and 59. */ - GET_LONG_RANGE0(min, time_str, 59); - - /* Check sec field of time_str. */ - time_str = colon + 1; - if (*time_str == '\0') - return -1; - - /* Sec must be between 0 and 59. */ - GET_LONG_RANGE0(sec, time_str, 59); - - /* Check day_str. Day must be <1-31>. */ - GET_LONG_RANGE(day, day_str, 1, 31); - - /* Check month_str. Month must match month_name. */ - month = 0; - if (strlen(month_str) >= 3) - for (i = 0; month_name[i]; i++) - if (strncmp(month_str, month_name[i], strlen(month_str)) - == 0) { - month = i; - break; - } - if (!month_name[i]) - return -1; - - /* Check year_str. Year must be <1993-2035>. */ - GET_LONG_RANGE(year, year_str, 1993, 2035); - - memset(&tm, 0, sizeof(tm)); - tm.tm_sec = sec; - tm.tm_min = min; - tm.tm_hour = hour; - tm.tm_mon = month; - tm.tm_mday = day; - tm.tm_year = year - 1900; - - time = mktime(&tm); - - return time; -#undef GET_LONG_RANGE -} - -static int key_lifetime_set(struct vty *vty, struct key_range *krange, - const char *stime_str, const char *sday_str, - const char *smonth_str, const char *syear_str, - const char *etime_str, const char *eday_str, - const char *emonth_str, const char *eyear_str) -{ - time_t time_start; - time_t time_end; - - time_start = key_str2time(stime_str, sday_str, smonth_str, syear_str); - if (time_start < 0) { - vty_out(vty, "Malformed time value\n"); - return CMD_WARNING_CONFIG_FAILED; - } - time_end = key_str2time(etime_str, eday_str, emonth_str, eyear_str); - - if (time_end < 0) { - vty_out(vty, "Malformed time value\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (time_end <= time_start) { - vty_out(vty, "Expire time is not later than start time\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - krange->start = time_start; - krange->end = time_end; - - return CMD_SUCCESS; -} - -static int key_lifetime_duration_set(struct vty *vty, struct key_range *krange, - const char *stime_str, - const char *sday_str, - const char *smonth_str, - const char *syear_str, - const char *duration_str) -{ - time_t time_start; - uint32_t duration; - - time_start = key_str2time(stime_str, sday_str, smonth_str, syear_str); - if (time_start < 0) { - vty_out(vty, "Malformed time value\n"); - return CMD_WARNING_CONFIG_FAILED; - } - krange->start = time_start; - - duration = strtoul(duration_str, NULL, 10); - krange->duration = 1; - krange->end = time_start + duration; - - return CMD_SUCCESS; -} - -static int key_lifetime_infinite_set(struct vty *vty, struct key_range *krange, - const char *stime_str, - const char *sday_str, - const char *smonth_str, - const char *syear_str) -{ - time_t time_start; - - time_start = key_str2time(stime_str, sday_str, smonth_str, syear_str); - if (time_start < 0) { - vty_out(vty, "Malformed time value\n"); - return CMD_WARNING_CONFIG_FAILED; - } - krange->start = time_start; - - krange->end = -1; - - return CMD_SUCCESS; -} - -DEFUN (accept_lifetime_day_month_day_month, - accept_lifetime_day_month_day_month_cmd, - "accept-lifetime HH:MM:SS (1-31) MONTH (1993-2035) HH:MM:SS (1-31) MONTH (1993-2035)", - "Set accept lifetime of the key\n" - "Time to start\n" - "Day of th month to start\n" - "Month of the year to start\n" - "Year to start\n" - "Time to expire\n" - "Day of th month to expire\n" - "Month of the year to expire\n" - "Year to expire\n") -{ - int idx_hhmmss = 1; - int idx_number = 2; - int idx_month = 3; - int idx_number_2 = 4; - int idx_hhmmss_2 = 5; - int idx_number_3 = 6; - int idx_month_2 = 7; - int idx_number_4 = 8; - VTY_DECLVAR_CONTEXT_SUB(key, key); - - return key_lifetime_set( - vty, &key->accept, argv[idx_hhmmss]->arg, argv[idx_number]->arg, - argv[idx_month]->arg, argv[idx_number_2]->arg, - argv[idx_hhmmss_2]->arg, argv[idx_number_3]->arg, - argv[idx_month_2]->arg, argv[idx_number_4]->arg); -} - -DEFUN (accept_lifetime_day_month_month_day, - accept_lifetime_day_month_month_day_cmd, - "accept-lifetime HH:MM:SS (1-31) MONTH (1993-2035) HH:MM:SS MONTH (1-31) (1993-2035)", - "Set accept lifetime of the key\n" - "Time to start\n" - "Day of th month to start\n" - "Month of the year to start\n" - "Year to start\n" - "Time to expire\n" - "Month of the year to expire\n" - "Day of th month to expire\n" - "Year to expire\n") -{ - int idx_hhmmss = 1; - int idx_number = 2; - int idx_month = 3; - int idx_number_2 = 4; - int idx_hhmmss_2 = 5; - int idx_month_2 = 6; - int idx_number_3 = 7; - int idx_number_4 = 8; - VTY_DECLVAR_CONTEXT_SUB(key, key); - - return key_lifetime_set( - vty, &key->accept, argv[idx_hhmmss]->arg, argv[idx_number]->arg, - argv[idx_month]->arg, argv[idx_number_2]->arg, - argv[idx_hhmmss_2]->arg, argv[idx_number_3]->arg, - argv[idx_month_2]->arg, argv[idx_number_4]->arg); -} - -DEFUN (accept_lifetime_month_day_day_month, - accept_lifetime_month_day_day_month_cmd, - "accept-lifetime HH:MM:SS MONTH (1-31) (1993-2035) HH:MM:SS (1-31) MONTH (1993-2035)", - "Set accept lifetime of the key\n" - "Time to start\n" - "Month of the year to start\n" - "Day of th month to start\n" - "Year to start\n" - "Time to expire\n" - "Day of th month to expire\n" - "Month of the year to expire\n" - "Year to expire\n") -{ - int idx_hhmmss = 1; - int idx_month = 2; - int idx_number = 3; - int idx_number_2 = 4; - int idx_hhmmss_2 = 5; - int idx_number_3 = 6; - int idx_month_2 = 7; - int idx_number_4 = 8; - VTY_DECLVAR_CONTEXT_SUB(key, key); - - return key_lifetime_set( - vty, &key->accept, argv[idx_hhmmss]->arg, argv[idx_number]->arg, - argv[idx_month]->arg, argv[idx_number_2]->arg, - argv[idx_hhmmss_2]->arg, argv[idx_number_3]->arg, - argv[idx_month_2]->arg, argv[idx_number_4]->arg); -} - -DEFUN (accept_lifetime_month_day_month_day, - accept_lifetime_month_day_month_day_cmd, - "accept-lifetime HH:MM:SS MONTH (1-31) (1993-2035) HH:MM:SS MONTH (1-31) (1993-2035)", - "Set accept lifetime of the key\n" - "Time to start\n" - "Month of the year to start\n" - "Day of th month to start\n" - "Year to start\n" - "Time to expire\n" - "Month of the year to expire\n" - "Day of th month to expire\n" - "Year to expire\n") -{ - int idx_hhmmss = 1; - int idx_month = 2; - int idx_number = 3; - int idx_number_2 = 4; - int idx_hhmmss_2 = 5; - int idx_month_2 = 6; - int idx_number_3 = 7; - int idx_number_4 = 8; - VTY_DECLVAR_CONTEXT_SUB(key, key); - - return key_lifetime_set( - vty, &key->accept, argv[idx_hhmmss]->arg, argv[idx_number]->arg, - argv[idx_month]->arg, argv[idx_number_2]->arg, - argv[idx_hhmmss_2]->arg, argv[idx_number_3]->arg, - argv[idx_month_2]->arg, argv[idx_number_4]->arg); -} - -DEFUN (accept_lifetime_infinite_day_month, - accept_lifetime_infinite_day_month_cmd, - "accept-lifetime HH:MM:SS (1-31) MONTH (1993-2035) infinite", - "Set accept lifetime of the key\n" - "Time to start\n" - "Day of th month to start\n" - "Month of the year to start\n" - "Year to start\n" - "Never expires\n") -{ - int idx_hhmmss = 1; - int idx_number = 2; - int idx_month = 3; - int idx_number_2 = 4; - VTY_DECLVAR_CONTEXT_SUB(key, key); - - return key_lifetime_infinite_set( - vty, &key->accept, argv[idx_hhmmss]->arg, argv[idx_number]->arg, - argv[idx_month]->arg, argv[idx_number_2]->arg); -} - -DEFUN (accept_lifetime_infinite_month_day, - accept_lifetime_infinite_month_day_cmd, - "accept-lifetime HH:MM:SS MONTH (1-31) (1993-2035) infinite", - "Set accept lifetime of the key\n" - "Time to start\n" - "Month of the year to start\n" - "Day of th month to start\n" - "Year to start\n" - "Never expires\n") -{ - int idx_hhmmss = 1; - int idx_month = 2; - int idx_number = 3; - int idx_number_2 = 4; - VTY_DECLVAR_CONTEXT_SUB(key, key); - - return key_lifetime_infinite_set( - vty, &key->accept, argv[idx_hhmmss]->arg, argv[idx_number]->arg, - argv[idx_month]->arg, argv[idx_number_2]->arg); -} - -DEFUN (accept_lifetime_duration_day_month, - accept_lifetime_duration_day_month_cmd, - "accept-lifetime HH:MM:SS (1-31) MONTH (1993-2035) duration (1-2147483646)", - "Set accept lifetime of the key\n" - "Time to start\n" - "Day of th month to start\n" - "Month of the year to start\n" - "Year to start\n" - "Duration of the key\n" - "Duration seconds\n") -{ - int idx_hhmmss = 1; - int idx_number = 2; - int idx_month = 3; - int idx_number_2 = 4; - int idx_number_3 = 6; - VTY_DECLVAR_CONTEXT_SUB(key, key); - - return key_lifetime_duration_set( - vty, &key->accept, argv[idx_hhmmss]->arg, argv[idx_number]->arg, - argv[idx_month]->arg, argv[idx_number_2]->arg, - argv[idx_number_3]->arg); -} - -DEFUN (accept_lifetime_duration_month_day, - accept_lifetime_duration_month_day_cmd, - "accept-lifetime HH:MM:SS MONTH (1-31) (1993-2035) duration (1-2147483646)", - "Set accept lifetime of the key\n" - "Time to start\n" - "Month of the year to start\n" - "Day of th month to start\n" - "Year to start\n" - "Duration of the key\n" - "Duration seconds\n") -{ - int idx_hhmmss = 1; - int idx_month = 2; - int idx_number = 3; - int idx_number_2 = 4; - int idx_number_3 = 6; - VTY_DECLVAR_CONTEXT_SUB(key, key); - - return key_lifetime_duration_set( - vty, &key->accept, argv[idx_hhmmss]->arg, argv[idx_number]->arg, - argv[idx_month]->arg, argv[idx_number_2]->arg, - argv[idx_number_3]->arg); -} - -DEFUN (no_accept_lifetime, - no_accept_lifetime_cmd, - "no accept-lifetime", - NO_STR - "Unset accept-lifetime\n") -{ - VTY_DECLVAR_CONTEXT_SUB(key, key); - - if (key->accept.start) - key->accept.start = 0; - if (key->accept.end) - key->accept.end = 0; - if (key->accept.duration) - key->accept.duration = 0; - - return CMD_SUCCESS; -} - -DEFUN (send_lifetime_day_month_day_month, - send_lifetime_day_month_day_month_cmd, - "send-lifetime HH:MM:SS (1-31) MONTH (1993-2035) HH:MM:SS (1-31) MONTH (1993-2035)", - "Set send lifetime of the key\n" - "Time to start\n" - "Day of th month to start\n" - "Month of the year to start\n" - "Year to start\n" - "Time to expire\n" - "Day of th month to expire\n" - "Month of the year to expire\n" - "Year to expire\n") -{ - int idx_hhmmss = 1; - int idx_number = 2; - int idx_month = 3; - int idx_number_2 = 4; - int idx_hhmmss_2 = 5; - int idx_number_3 = 6; - int idx_month_2 = 7; - int idx_number_4 = 8; - VTY_DECLVAR_CONTEXT_SUB(key, key); - - return key_lifetime_set( - vty, &key->send, argv[idx_hhmmss]->arg, argv[idx_number]->arg, - argv[idx_month]->arg, argv[idx_number_2]->arg, - argv[idx_hhmmss_2]->arg, argv[idx_number_3]->arg, - argv[idx_month_2]->arg, argv[idx_number_4]->arg); -} - -DEFUN (send_lifetime_day_month_month_day, - send_lifetime_day_month_month_day_cmd, - "send-lifetime HH:MM:SS (1-31) MONTH (1993-2035) HH:MM:SS MONTH (1-31) (1993-2035)", - "Set send lifetime of the key\n" - "Time to start\n" - "Day of th month to start\n" - "Month of the year to start\n" - "Year to start\n" - "Time to expire\n" - "Month of the year to expire\n" - "Day of th month to expire\n" - "Year to expire\n") -{ - int idx_hhmmss = 1; - int idx_number = 2; - int idx_month = 3; - int idx_number_2 = 4; - int idx_hhmmss_2 = 5; - int idx_month_2 = 6; - int idx_number_3 = 7; - int idx_number_4 = 8; - VTY_DECLVAR_CONTEXT_SUB(key, key); - - return key_lifetime_set( - vty, &key->send, argv[idx_hhmmss]->arg, argv[idx_number]->arg, - argv[idx_month]->arg, argv[idx_number_2]->arg, - argv[idx_hhmmss_2]->arg, argv[idx_number_3]->arg, - argv[idx_month_2]->arg, argv[idx_number_4]->arg); -} - -DEFUN (send_lifetime_month_day_day_month, - send_lifetime_month_day_day_month_cmd, - "send-lifetime HH:MM:SS MONTH (1-31) (1993-2035) HH:MM:SS (1-31) MONTH (1993-2035)", - "Set send lifetime of the key\n" - "Time to start\n" - "Month of the year to start\n" - "Day of th month to start\n" - "Year to start\n" - "Time to expire\n" - "Day of th month to expire\n" - "Month of the year to expire\n" - "Year to expire\n") -{ - int idx_hhmmss = 1; - int idx_month = 2; - int idx_number = 3; - int idx_number_2 = 4; - int idx_hhmmss_2 = 5; - int idx_number_3 = 6; - int idx_month_2 = 7; - int idx_number_4 = 8; - VTY_DECLVAR_CONTEXT_SUB(key, key); - - return key_lifetime_set( - vty, &key->send, argv[idx_hhmmss]->arg, argv[idx_number]->arg, - argv[idx_month]->arg, argv[idx_number_2]->arg, - argv[idx_hhmmss_2]->arg, argv[idx_number_3]->arg, - argv[idx_month_2]->arg, argv[idx_number_4]->arg); -} - -DEFUN (send_lifetime_month_day_month_day, - send_lifetime_month_day_month_day_cmd, - "send-lifetime HH:MM:SS MONTH (1-31) (1993-2035) HH:MM:SS MONTH (1-31) (1993-2035)", - "Set send lifetime of the key\n" - "Time to start\n" - "Month of the year to start\n" - "Day of th month to start\n" - "Year to start\n" - "Time to expire\n" - "Month of the year to expire\n" - "Day of th month to expire\n" - "Year to expire\n") -{ - int idx_hhmmss = 1; - int idx_month = 2; - int idx_number = 3; - int idx_number_2 = 4; - int idx_hhmmss_2 = 5; - int idx_month_2 = 6; - int idx_number_3 = 7; - int idx_number_4 = 8; - VTY_DECLVAR_CONTEXT_SUB(key, key); - - return key_lifetime_set( - vty, &key->send, argv[idx_hhmmss]->arg, argv[idx_number]->arg, - argv[idx_month]->arg, argv[idx_number_2]->arg, - argv[idx_hhmmss_2]->arg, argv[idx_number_3]->arg, - argv[idx_month_2]->arg, argv[idx_number_4]->arg); -} - -DEFUN (send_lifetime_infinite_day_month, - send_lifetime_infinite_day_month_cmd, - "send-lifetime HH:MM:SS (1-31) MONTH (1993-2035) infinite", - "Set send lifetime of the key\n" - "Time to start\n" - "Day of th month to start\n" - "Month of the year to start\n" - "Year to start\n" - "Never expires\n") -{ - int idx_hhmmss = 1; - int idx_number = 2; - int idx_month = 3; - int idx_number_2 = 4; - VTY_DECLVAR_CONTEXT_SUB(key, key); - - return key_lifetime_infinite_set( - vty, &key->send, argv[idx_hhmmss]->arg, argv[idx_number]->arg, - argv[idx_month]->arg, argv[idx_number_2]->arg); -} - -DEFUN (send_lifetime_infinite_month_day, - send_lifetime_infinite_month_day_cmd, - "send-lifetime HH:MM:SS MONTH (1-31) (1993-2035) infinite", - "Set send lifetime of the key\n" - "Time to start\n" - "Month of the year to start\n" - "Day of th month to start\n" - "Year to start\n" - "Never expires\n") -{ - int idx_hhmmss = 1; - int idx_month = 2; - int idx_number = 3; - int idx_number_2 = 4; - VTY_DECLVAR_CONTEXT_SUB(key, key); - - return key_lifetime_infinite_set( - vty, &key->send, argv[idx_hhmmss]->arg, argv[idx_number]->arg, - argv[idx_month]->arg, argv[idx_number_2]->arg); -} - -DEFUN (send_lifetime_duration_day_month, - send_lifetime_duration_day_month_cmd, - "send-lifetime HH:MM:SS (1-31) MONTH (1993-2035) duration (1-2147483646)", - "Set send lifetime of the key\n" - "Time to start\n" - "Day of th month to start\n" - "Month of the year to start\n" - "Year to start\n" - "Duration of the key\n" - "Duration seconds\n") -{ - int idx_hhmmss = 1; - int idx_number = 2; - int idx_month = 3; - int idx_number_2 = 4; - int idx_number_3 = 6; - VTY_DECLVAR_CONTEXT_SUB(key, key); - - return key_lifetime_duration_set( - vty, &key->send, argv[idx_hhmmss]->arg, argv[idx_number]->arg, - argv[idx_month]->arg, argv[idx_number_2]->arg, - argv[idx_number_3]->arg); -} - -DEFUN (send_lifetime_duration_month_day, - send_lifetime_duration_month_day_cmd, - "send-lifetime HH:MM:SS MONTH (1-31) (1993-2035) duration (1-2147483646)", - "Set send lifetime of the key\n" - "Time to start\n" - "Month of the year to start\n" - "Day of th month to start\n" - "Year to start\n" - "Duration of the key\n" - "Duration seconds\n") -{ - int idx_hhmmss = 1; - int idx_month = 2; - int idx_number = 3; - int idx_number_2 = 4; - int idx_number_3 = 6; - VTY_DECLVAR_CONTEXT_SUB(key, key); - - return key_lifetime_duration_set( - vty, &key->send, argv[idx_hhmmss]->arg, argv[idx_number]->arg, - argv[idx_month]->arg, argv[idx_number_2]->arg, - argv[idx_number_3]->arg); -} - -DEFUN (no_send_lifetime, - no_send_lifetime_cmd, - "no send-lifetime", - NO_STR - "Unset send-lifetime\n") -{ - VTY_DECLVAR_CONTEXT_SUB(key, key); - - if (key->send.start) - key->send.start = 0; - if (key->send.end) - key->send.end = 0; - if (key->send.duration) - key->send.duration = 0; - - return CMD_SUCCESS; -} - -static int keychain_config_write(struct vty *vty); -static struct cmd_node keychain_node = { - .name = "keychain", - .node = KEYCHAIN_NODE, - .parent_node = CONFIG_NODE, - .prompt = "%s(config-keychain)# ", - .config_write = keychain_config_write, -}; - -static struct cmd_node keychain_key_node = { - .name = "keychain key", - .node = KEYCHAIN_KEY_NODE, - .parent_node = KEYCHAIN_NODE, - .prompt = "%s(config-keychain-key)# ", -}; - -static int keychain_strftime(char *buf, int bufsiz, time_t *time) -{ - struct tm tm; - size_t len; - - localtime_r(time, &tm); - - len = strftime(buf, bufsiz, "%T %b %d %Y", &tm); - - return len; -} - -static int keychain_config_write(struct vty *vty) -{ - struct keychain *keychain; - struct key *key; - struct listnode *node; - struct listnode *knode; - char buf[BUFSIZ]; - - for (ALL_LIST_ELEMENTS_RO(keychain_list, node, keychain)) { - vty_out(vty, "key chain %s\n", keychain->name); - - for (ALL_LIST_ELEMENTS_RO(keychain->key, knode, key)) { - vty_out(vty, " key %d\n", key->index); - - if (key->string) - vty_out(vty, " key-string %s\n", key->string); - - if (key->hash_algo != KEYCHAIN_ALGO_NULL) - vty_out(vty, " cryptographic-algorithm %s\n", - keychain_get_algo_name_by_id( - key->hash_algo)); - - if (key->accept.start) { - keychain_strftime(buf, BUFSIZ, - &key->accept.start); - vty_out(vty, " accept-lifetime %s", buf); - - if (key->accept.end == -1) - vty_out(vty, " infinite"); - else if (key->accept.duration) - vty_out(vty, " duration %ld", - (long)(key->accept.end - - key->accept.start)); - else { - keychain_strftime(buf, BUFSIZ, - &key->accept.end); - vty_out(vty, " %s", buf); - } - vty_out(vty, "\n"); - } - - if (key->send.start) { - keychain_strftime(buf, BUFSIZ, - &key->send.start); - vty_out(vty, " send-lifetime %s", buf); - - if (key->send.end == -1) - vty_out(vty, " infinite"); - else if (key->send.duration) - vty_out(vty, " duration %ld", - (long)(key->send.end - - key->send.start)); - else { - keychain_strftime(buf, BUFSIZ, - &key->send.end); - vty_out(vty, " %s", buf); - } - vty_out(vty, "\n"); - } - - vty_out(vty, " exit\n"); - } - vty_out(vty, "exit\n"); - vty_out(vty, "!\n"); - } - - return 0; -} - - -static void keychain_active_config(vector comps, struct cmd_token *token) -{ - struct keychain *keychain; - struct listnode *node; - - for (ALL_LIST_ELEMENTS_RO(keychain_list, node, keychain)) - vector_set(comps, XSTRDUP(MTYPE_COMPLETION, keychain->name)); -} - -static const struct cmd_variable_handler keychain_var_handlers[] = { - {.varname = "key_chain", .completions = keychain_active_config}, - {.tokenname = "KEYCHAIN_NAME", .completions = keychain_active_config}, - {.tokenname = "KCHAIN_NAME", .completions = keychain_active_config}, - {.completions = NULL} -}; - void keychain_terminate(void) { struct keychain *keychain; @@ -1202,70 +292,25 @@ void keychain_terminate(void) list_delete(&keychain_list); } -void keychain_init(void) +void keychain_init_new(bool in_backend) { keychain_list = list_new(); - /* Register handler for keychain auto config support */ - cmd_variable_handler_register(keychain_var_handlers); - install_node(&keychain_node); - install_node(&keychain_key_node); - - install_default(KEYCHAIN_NODE); - install_default(KEYCHAIN_KEY_NODE); - - install_element(CONFIG_NODE, &key_chain_cmd); - install_element(CONFIG_NODE, &no_key_chain_cmd); - install_element(KEYCHAIN_NODE, &key_cmd); - install_element(KEYCHAIN_NODE, &no_key_cmd); - - install_element(KEYCHAIN_NODE, &key_chain_cmd); - install_element(KEYCHAIN_NODE, &no_key_chain_cmd); - - install_element(KEYCHAIN_KEY_NODE, &key_string_cmd); - install_element(KEYCHAIN_KEY_NODE, &no_key_string_cmd); - - install_element(KEYCHAIN_KEY_NODE, &key_chain_cmd); - install_element(KEYCHAIN_KEY_NODE, &no_key_chain_cmd); - - install_element(KEYCHAIN_KEY_NODE, &key_cmd); - install_element(KEYCHAIN_KEY_NODE, &no_key_cmd); - - install_element(KEYCHAIN_KEY_NODE, - &accept_lifetime_day_month_day_month_cmd); - install_element(KEYCHAIN_KEY_NODE, - &accept_lifetime_day_month_month_day_cmd); - install_element(KEYCHAIN_KEY_NODE, - &accept_lifetime_month_day_day_month_cmd); - install_element(KEYCHAIN_KEY_NODE, - &accept_lifetime_month_day_month_day_cmd); - install_element(KEYCHAIN_KEY_NODE, - &accept_lifetime_infinite_day_month_cmd); - install_element(KEYCHAIN_KEY_NODE, - &accept_lifetime_infinite_month_day_cmd); - install_element(KEYCHAIN_KEY_NODE, - &accept_lifetime_duration_day_month_cmd); - install_element(KEYCHAIN_KEY_NODE, - &accept_lifetime_duration_month_day_cmd); - install_element(KEYCHAIN_KEY_NODE, &no_accept_lifetime_cmd); + if (!in_backend) + keychain_cli_init(); +} - install_element(KEYCHAIN_KEY_NODE, - &send_lifetime_day_month_day_month_cmd); - install_element(KEYCHAIN_KEY_NODE, - &send_lifetime_day_month_month_day_cmd); - install_element(KEYCHAIN_KEY_NODE, - &send_lifetime_month_day_day_month_cmd); - install_element(KEYCHAIN_KEY_NODE, - &send_lifetime_month_day_month_day_cmd); - install_element(KEYCHAIN_KEY_NODE, - &send_lifetime_infinite_day_month_cmd); - install_element(KEYCHAIN_KEY_NODE, - &send_lifetime_infinite_month_day_cmd); - install_element(KEYCHAIN_KEY_NODE, - &send_lifetime_duration_day_month_cmd); - install_element(KEYCHAIN_KEY_NODE, - &send_lifetime_duration_month_day_cmd); - install_element(KEYCHAIN_KEY_NODE, &no_send_lifetime_cmd); - install_element(KEYCHAIN_KEY_NODE, &cryptographic_algorithm_cmd); - install_element(KEYCHAIN_KEY_NODE, &no_cryptographic_algorithm_cmd); +void keychain_init(void) +{ + keychain_init_new(false); } + +const struct frr_yang_module_info ietf_key_chain_deviation_info = { + .name = "frr-deviations-ietf-key-chain", + .ignore_cfg_cbs = true, + .nodes = { + { + .xpath = NULL, + }, + }, +}; diff --git a/lib/keychain.h b/lib/keychain.h index c96b74e..dc35c2e 100644 --- a/lib/keychain.h +++ b/lib/keychain.h @@ -6,6 +6,8 @@ #ifndef _ZEBRA_KEYCHAIN_H #define _ZEBRA_KEYCHAIN_H +#include "memory.h" +#include "northbound.h" #include "qobj.h" #ifdef __cplusplus @@ -44,6 +46,10 @@ struct keychain_algo_info { const char *desc; }; +extern const struct frr_yang_module_info ietf_key_chain_info; +extern const struct frr_yang_module_info ietf_key_chain_cli_info; +extern const struct frr_yang_module_info ietf_key_chain_deviation_info; + extern const struct keychain_algo_info algo_info[]; uint16_t keychain_get_block_size(enum keychain_hash_algo key); uint16_t keychain_get_hash_len(enum keychain_hash_algo key); @@ -55,6 +61,8 @@ const char *keychain_get_algo_name_by_id(enum keychain_hash_algo key); struct keychain { char *name; + char *desc; + time_t last_touch; struct list *key; @@ -81,13 +89,43 @@ struct key { }; DECLARE_QOBJ_TYPE(key); +DECLARE_MTYPE(KEY); +DECLARE_MTYPE(KEYCHAIN); +DECLARE_MTYPE(KEYCHAIN_DESC); + +/* keychain implementation */ +extern struct list *keychain_list; +struct keychain *keychain_lookup(const char *name); +struct keychain *keychain_get(const char *name); +void keychain_delete(struct keychain *keychain); +struct key *key_lookup(const struct keychain *keychain, uint32_t index); +struct key *key_get(const struct keychain *keychain, uint32_t index); +void key_delete(struct keychain *keychain, struct key *key); + +void keychain_cli_init(void); +extern void key_chains_key_chain_cli_write(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +extern void key_chains_key_chain_cli_write_end(struct vty *vty, const struct lyd_node *dnode); +extern void key_chains_key_chain_description_cli_write(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +void key_chains_key_chain_key_cli_write(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +extern void key_chains_key_chain_key_cli_write_end(struct vty *vty, const struct lyd_node *dnode); +extern void key_chains_key_chain_key_lifetime_send_accept_lifetime_start_date_time_cli_write(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +extern void key_chains_key_chain_key_lifetime_send_lifetime_start_date_time_cli_write(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +extern void key_chains_key_chain_key_lifetime_accept_lifetime_start_date_time_cli_write(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +extern void key_chains_key_chain_key_crypto_algorithm_cli_write(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +extern void key_chains_key_chain_key_key_string_keystring_cli_write(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); + +/* keychain users */ extern void keychain_init(void); +extern void keychain_init_new(bool in_backend); extern void keychain_terminate(void); extern struct keychain *keychain_lookup(const char *); extern struct key *key_lookup_for_accept(const struct keychain *, uint32_t); extern struct key *key_match_for_accept(const struct keychain *, const char *); extern struct key *key_lookup_for_send(const struct keychain *); const char *keychain_algo_str(enum keychain_hash_algo hash_algo); + + + #ifdef __cplusplus } #endif diff --git a/lib/keychain_cli.c b/lib/keychain_cli.c new file mode 100644 index 0000000..26f56f1 --- /dev/null +++ b/lib/keychain_cli.c @@ -0,0 +1,1033 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * February 22 2024, Christian Hopps <chopps@labn.net> + * + * Copyright (C) 2024 LabN Consulting, L.L.C. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <zebra.h> +#include "command.h" +#include "keychain.h" +#include "northbound.h" +#include "northbound_cli.h" +#include "vty.h" + +#include "lib/keychain_cli_clippy.c" + +DEFPY_YANG_NOSH( + key_chain, + key_chain_cmd, + "key chain WORD", + "Authentication key management\n" + "Key-chain management\n" + "Key-chain name\n") +{ + char *xpath; + int ret; + + xpath = asprintfrr(MTYPE_TMP, + "/ietf-key-chain:key-chains/key-chain[name='%s']", + chain); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + ret = nb_cli_apply_changes(vty, NULL); + if (ret == CMD_SUCCESS) + VTY_PUSH_XPATH(KEYCHAIN_NODE, xpath); + XFREE(MTYPE_TMP, xpath); + return ret; +} + +DEFPY_YANG( + no_key_chain, + no_key_chain_cmd, + "no key chain WORD", + NO_STR + "Authentication key management\n" + "Key-chain management\n" + "Key-chain name\n") +{ + char *xpath; + + xpath = asprintfrr(MTYPE_TMP, + "/ietf-key-chain:key-chains/key-chain[name='%s']", + chain); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + XFREE(MTYPE_TMP, xpath); + return nb_cli_apply_changes_clear_pending(vty, NULL); +} + +DEFPY_YANG_NOSH( + key, + key_cmd, + "key (0-2147483647)", + "Configure a key\n" + "Key identifier number\n") +{ + char *xpath; + int ret; + + xpath = asprintfrr(MTYPE_TMP, "%s/key[key-id='%s']", VTY_CURR_XPATH, + key_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + ret = nb_cli_apply_changes(vty, NULL); + if (ret == CMD_SUCCESS) + VTY_PUSH_XPATH(KEYCHAIN_KEY_NODE, xpath); + XFREE(MTYPE_TMP, xpath); + return ret; +} + +DEFPY_YANG( + no_key, + no_key_cmd, + "no key (0-2147483647)", + NO_STR + "Delete a key\n" + "Key identifier number\n") +{ + char *xpath; + + xpath = asprintfrr(MTYPE_TMP, "%s/key[key-id='%s']", VTY_CURR_XPATH, + key_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + XFREE(MTYPE_TMP, xpath); + return nb_cli_apply_changes_clear_pending(vty, NULL); +} + +DEFPY_YANG( + key_string, + key_string_cmd, + "key-string LINE", + "Set key string\n" + "The key\n") +{ + nb_cli_enqueue_change(vty, "./key-string/keystring", NB_OP_CREATE, line); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_key_string, + no_key_string_cmd, + "no key-string [LINE]", + NO_STR + "Unset key string\n" + "The key\n") +{ + nb_cli_enqueue_change(vty, "./key-string/keystring", NB_OP_DESTROY, line); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + cryptographic_algorithm, + cryptographic_algorithm_cmd, + "cryptographic-algorithm " + "<md5|hmac-sha-1|hmac-sha-256|hmac-sha-384|hmac-sha-512>$algo", + "Cryptographic-algorithm\n" + "Use MD5 algorithm\n" + "Use HMAC-SHA-1 algorithm\n" + "Use HMAC-SHA-256 algorithm\n" + "Use HMAC-SHA-384 algorithm\n" + "Use HMAC-SHA-512 algorithm\n") +{ + nb_cli_enqueue_change(vty, "./crypto-algorithm", NB_OP_CREATE, algo); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_cryptographic_algorithm, + no_cryptographic_algorithm_cmd, + "no cryptographic-algorithm " + "[<md5|hmac-sha-1|hmac-sha-256|hmac-sha-384|hmac-sha-512>$algo]", + NO_STR + "Cryptographic-algorithm\n" + "Use MD5 algorithm\n" + "Use HMAC-SHA-1 algorithm\n" + "Use HMAC-SHA-256 algorithm\n" + "Use HMAC-SHA-384 algorithm\n" + "Use HMAC-SHA-512 algorithm\n") +{ + nb_cli_enqueue_change(vty, "./crypto-algorithm", NB_OP_DESTROY, algo); + return nb_cli_apply_changes(vty, NULL); +} + +const char *month_name[] = { + "january", "february", "march", "april", "may", "june", "july", + "august", "september", "october", "november", "december", NULL +}; + +static int __get_month(const char *month_str) +{ + int i, len; + + len = strlen(month_str); + if (len < 3) + return -1; + for (i = 1; month_name[i-1]; i++) + if (strncasecmp(month_str, month_name[i-1], len) == 0) + return i; + return -1; +} + + +static long __timezone_offset(void) +{ + time_t now; + struct tm *tm_now; + + time(&now); + tm_now = localtime(&now); + return tm_now->tm_gmtoff; +} + +static int __lifetime_set(struct vty *vty, char timebuf[32], + const char *time_node, const char *leaf_node, + const char *time_str, const char *day_str, + const char *month_str, const char *year_str) +{ + char xpath[128]; + int month = __get_month(month_str); + int hoff, moff; + long offset; + + if (month < 1) { + vty_out(vty, "Bad month value: %s\n", month_str); + return -1; + } + + offset = __timezone_offset(); + hoff = offset / 3600; + if (offset < 0) + offset = -offset; + moff = (offset % 3600) / 60; + + snprintf(timebuf, 32, "%s-%02d-%02dT%s%+03d:%02d", year_str, month, + atoi(day_str), time_str, hoff, moff); + snprintf(xpath, sizeof(xpath), "./lifetime/%s/%s", time_node, leaf_node); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, timebuf); + return 0; +} + + +static int key_lifetime_set(struct vty *vty, const char *time_node, + const char *stime_str, const char *sday_str, + const char *smonth_str, const char *syear_str, + const char *etime_str, const char *eday_str, + const char *emonth_str, const char *eyear_str) +{ + char time1[32]; + char time2[32]; + + if (__lifetime_set(vty, time1, time_node, "start-date-time", stime_str, + sday_str, smonth_str, syear_str)) + return CMD_WARNING_CONFIG_FAILED; + + if (__lifetime_set(vty, time2, time_node, "end-date-time", etime_str, + eday_str, emonth_str, eyear_str)) + return CMD_WARNING_CONFIG_FAILED; + + return nb_cli_apply_changes(vty, NULL); +} + +static int key_lifetime_duration_set(struct vty *vty, const char *time_node, + const char *stime_str, const char *sday_str, + const char *smonth_str, + const char *syear_str, + const char *duration_str) +{ + char xpath[128]; + char time[32]; + + if (__lifetime_set(vty, time, time_node, "start-date-time", stime_str, + sday_str, smonth_str, syear_str)) + return CMD_WARNING_CONFIG_FAILED; + + /* End time. */ + snprintf(xpath, sizeof(xpath), "./lifetime/%s/duration", time_node); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, duration_str); + + return nb_cli_apply_changes(vty, NULL); +} + +static int key_lifetime_infinite_set(struct vty *vty, const char *time_node, + const char *stime_str, const char *sday_str, + const char *smonth_str, + const char *syear_str) +{ + char xpath[128]; + char time[32]; + + if (__lifetime_set(vty, time, time_node, "start-date-time", stime_str, + sday_str, smonth_str, syear_str)) + return CMD_WARNING_CONFIG_FAILED; + + /* End time. */ + snprintf(xpath, sizeof(xpath), "./lifetime/%s/no-end-time", time_node); + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + accept_lifetime_day_month_day_month, + accept_lifetime_day_month_day_month_cmd, + "accept-lifetime HH:MM:SS (1-31) MONTH (1993-2035) HH:MM:SS (1-31) MONTH (1993-2035)", + "Set accept lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Time to expire\n" + "Day of th month to expire\n" + "Month of the year to expire\n" + "Year to expire\n") +{ + int idx_hhmmss = 1; + int idx_number = 2; + int idx_month = 3; + int idx_number_2 = 4; + int idx_hhmmss_2 = 5; + int idx_number_3 = 6; + int idx_month_2 = 7; + int idx_number_4 = 8; + + return key_lifetime_set(vty, "accept-lifetime", argv[idx_hhmmss]->arg, + argv[idx_number]->arg, argv[idx_month]->arg, + argv[idx_number_2]->arg, argv[idx_hhmmss_2]->arg, + argv[idx_number_3]->arg, argv[idx_month_2]->arg, + argv[idx_number_4]->arg); +} + +DEFPY_YANG(accept_lifetime_day_month_month_day, + accept_lifetime_day_month_month_day_cmd, + "accept-lifetime HH:MM:SS (1-31) MONTH (1993-2035) HH:MM:SS MONTH (1-31) (1993-2035)", + "Set accept lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Time to expire\n" + "Month of the year to expire\n" + "Day of th month to expire\n" + "Year to expire\n") +{ + int idx_hhmmss = 1; + int idx_number = 2; + int idx_month = 3; + int idx_number_2 = 4; + int idx_hhmmss_2 = 5; + int idx_month_2 = 6; + int idx_number_3 = 7; + int idx_number_4 = 8; + + return key_lifetime_set(vty, "accept-lifetime", argv[idx_hhmmss]->arg, + argv[idx_number]->arg, argv[idx_month]->arg, + argv[idx_number_2]->arg, argv[idx_hhmmss_2]->arg, + argv[idx_number_3]->arg, argv[idx_month_2]->arg, + argv[idx_number_4]->arg); +} + +DEFPY_YANG(accept_lifetime_month_day_day_month, + accept_lifetime_month_day_day_month_cmd, + "accept-lifetime HH:MM:SS MONTH (1-31) (1993-2035) HH:MM:SS (1-31) MONTH (1993-2035)", + "Set accept lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Time to expire\n" + "Day of th month to expire\n" + "Month of the year to expire\n" + "Year to expire\n") +{ + int idx_hhmmss = 1; + int idx_month = 2; + int idx_number = 3; + int idx_number_2 = 4; + int idx_hhmmss_2 = 5; + int idx_number_3 = 6; + int idx_month_2 = 7; + int idx_number_4 = 8; + + return key_lifetime_set(vty, "accept-lifetime", argv[idx_hhmmss]->arg, + argv[idx_number]->arg, argv[idx_month]->arg, + argv[idx_number_2]->arg, argv[idx_hhmmss_2]->arg, + argv[idx_number_3]->arg, argv[idx_month_2]->arg, + argv[idx_number_4]->arg); +} + +DEFPY_YANG(accept_lifetime_month_day_month_day, + accept_lifetime_month_day_month_day_cmd, + "accept-lifetime HH:MM:SS MONTH (1-31) (1993-2035) HH:MM:SS MONTH (1-31) (1993-2035)", + "Set accept lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Time to expire\n" + "Month of the year to expire\n" + "Day of th month to expire\n" + "Year to expire\n") +{ + int idx_hhmmss = 1; + int idx_month = 2; + int idx_number = 3; + int idx_number_2 = 4; + int idx_hhmmss_2 = 5; + int idx_month_2 = 6; + int idx_number_3 = 7; + int idx_number_4 = 8; + + return key_lifetime_set(vty, "accept-lifetime", argv[idx_hhmmss]->arg, + argv[idx_number]->arg, argv[idx_month]->arg, + argv[idx_number_2]->arg, argv[idx_hhmmss_2]->arg, + argv[idx_number_3]->arg, argv[idx_month_2]->arg, + argv[idx_number_4]->arg); +} + +DEFPY_YANG(accept_lifetime_infinite_day_month, + accept_lifetime_infinite_day_month_cmd, + "accept-lifetime HH:MM:SS (1-31) MONTH (1993-2035) infinite", + "Set accept lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Never expires\n") +{ + int idx_hhmmss = 1; + int idx_number = 2; + int idx_month = 3; + int idx_number_2 = 4; + + return key_lifetime_infinite_set(vty, "accept-lifetime", + argv[idx_hhmmss]->arg, + argv[idx_number]->arg, + argv[idx_month]->arg, + argv[idx_number_2]->arg); +} + +DEFPY_YANG(accept_lifetime_infinite_month_day, + accept_lifetime_infinite_month_day_cmd, + "accept-lifetime HH:MM:SS MONTH (1-31) (1993-2035) infinite", + "Set accept lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Never expires\n") +{ + int idx_hhmmss = 1; + int idx_month = 2; + int idx_number = 3; + int idx_number_2 = 4; + + return key_lifetime_infinite_set(vty, "accept-lifetime", + argv[idx_hhmmss]->arg, + argv[idx_number]->arg, + argv[idx_month]->arg, + argv[idx_number_2]->arg); +} + +DEFPY_YANG(accept_lifetime_duration_day_month, + accept_lifetime_duration_day_month_cmd, + "accept-lifetime HH:MM:SS (1-31) MONTH (1993-2035) duration (1-2147483646)", + "Set accept lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Duration of the key\n" + "Duration seconds\n") +{ + int idx_hhmmss = 1; + int idx_number = 2; + int idx_month = 3; + int idx_number_2 = 4; + int idx_number_3 = 6; + + return key_lifetime_duration_set(vty, "accept-lifetime", + argv[idx_hhmmss]->arg, + argv[idx_number]->arg, + argv[idx_month]->arg, + argv[idx_number_2]->arg, + argv[idx_number_3]->arg); +} + +DEFPY_YANG(accept_lifetime_duration_month_day, + accept_lifetime_duration_month_day_cmd, + "accept-lifetime HH:MM:SS MONTH (1-31) (1993-2035) duration (1-2147483646)", + "Set accept lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Duration of the key\n" + "Duration seconds\n") +{ + int idx_hhmmss = 1; + int idx_month = 2; + int idx_number = 3; + int idx_number_2 = 4; + int idx_number_3 = 6; + + return key_lifetime_duration_set(vty, "accept-lifetime", + argv[idx_hhmmss]->arg, + argv[idx_number]->arg, + argv[idx_month]->arg, + argv[idx_number_2]->arg, + argv[idx_number_3]->arg); +} + +DEFPY_YANG(no_accept_lifetime, + no_accept_lifetime_cmd, + "no accept-lifetime", + NO_STR + "Unset accept-lifetime\n") +{ + nb_cli_enqueue_change(vty, "accept-lifetime", NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + send_lifetime_day_month_day_month, send_lifetime_day_month_day_month_cmd, + "send-lifetime HH:MM:SS (1-31) MONTH (1993-2035) HH:MM:SS (1-31) MONTH (1993-2035)", + "Set send lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Time to expire\n" + "Day of th month to expire\n" + "Month of the year to expire\n" + "Year to expire\n") +{ + int idx_hhmmss = 1; + int idx_number = 2; + int idx_month = 3; + int idx_number_2 = 4; + int idx_hhmmss_2 = 5; + int idx_number_3 = 6; + int idx_month_2 = 7; + int idx_number_4 = 8; + + return key_lifetime_set(vty, "send-lifetime", argv[idx_hhmmss]->arg, + argv[idx_number]->arg, argv[idx_month]->arg, + argv[idx_number_2]->arg, argv[idx_hhmmss_2]->arg, + argv[idx_number_3]->arg, argv[idx_month_2]->arg, + argv[idx_number_4]->arg); +} + +DEFPY_YANG(send_lifetime_day_month_month_day, + send_lifetime_day_month_month_day_cmd, + "send-lifetime HH:MM:SS (1-31) MONTH (1993-2035) HH:MM:SS MONTH (1-31) (1993-2035)", + "Set send lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Time to expire\n" + "Month of the year to expire\n" + "Day of th month to expire\n" + "Year to expire\n") +{ + int idx_hhmmss = 1; + int idx_number = 2; + int idx_month = 3; + int idx_number_2 = 4; + int idx_hhmmss_2 = 5; + int idx_month_2 = 6; + int idx_number_3 = 7; + int idx_number_4 = 8; + + return key_lifetime_set(vty, "send-lifetime", argv[idx_hhmmss]->arg, + argv[idx_number]->arg, argv[idx_month]->arg, + argv[idx_number_2]->arg, argv[idx_hhmmss_2]->arg, + argv[idx_number_3]->arg, argv[idx_month_2]->arg, + argv[idx_number_4]->arg); +} + +DEFPY_YANG(send_lifetime_month_day_day_month, + send_lifetime_month_day_day_month_cmd, + "send-lifetime HH:MM:SS MONTH (1-31) (1993-2035) HH:MM:SS (1-31) MONTH (1993-2035)", + "Set send lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Time to expire\n" + "Day of th month to expire\n" + "Month of the year to expire\n" + "Year to expire\n") +{ + int idx_hhmmss = 1; + int idx_month = 2; + int idx_number = 3; + int idx_number_2 = 4; + int idx_hhmmss_2 = 5; + int idx_number_3 = 6; + int idx_month_2 = 7; + int idx_number_4 = 8; + + return key_lifetime_set(vty, "send-lifetime", argv[idx_hhmmss]->arg, + argv[idx_number]->arg, argv[idx_month]->arg, + argv[idx_number_2]->arg, argv[idx_hhmmss_2]->arg, + argv[idx_number_3]->arg, argv[idx_month_2]->arg, + argv[idx_number_4]->arg); +} + +DEFPY_YANG(send_lifetime_month_day_month_day, + send_lifetime_month_day_month_day_cmd, + "send-lifetime HH:MM:SS MONTH (1-31) (1993-2035) HH:MM:SS MONTH (1-31) (1993-2035)", + "Set send lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Time to expire\n" + "Month of the year to expire\n" + "Day of th month to expire\n" + "Year to expire\n") +{ + int idx_hhmmss = 1; + int idx_month = 2; + int idx_number = 3; + int idx_number_2 = 4; + int idx_hhmmss_2 = 5; + int idx_month_2 = 6; + int idx_number_3 = 7; + int idx_number_4 = 8; + + return key_lifetime_set(vty, "send-lifetime", argv[idx_hhmmss]->arg, + argv[idx_number]->arg, argv[idx_month]->arg, + argv[idx_number_2]->arg, argv[idx_hhmmss_2]->arg, + argv[idx_number_3]->arg, argv[idx_month_2]->arg, + argv[idx_number_4]->arg); +} + +DEFPY_YANG(send_lifetime_infinite_day_month, + send_lifetime_infinite_day_month_cmd, + "send-lifetime HH:MM:SS (1-31) MONTH (1993-2035) infinite", + "Set send lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Never expires\n") +{ + int idx_hhmmss = 1; + int idx_number = 2; + int idx_month = 3; + int idx_number_2 = 4; + + return key_lifetime_infinite_set(vty, "send-lifetime", + argv[idx_hhmmss]->arg, + argv[idx_number]->arg, + argv[idx_month]->arg, + argv[idx_number_2]->arg); +} + +DEFPY_YANG(send_lifetime_infinite_month_day, + send_lifetime_infinite_month_day_cmd, + "send-lifetime HH:MM:SS MONTH (1-31) (1993-2035) infinite", + "Set send lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Never expires\n") +{ + int idx_hhmmss = 1; + int idx_month = 2; + int idx_number = 3; + int idx_number_2 = 4; + + return key_lifetime_infinite_set(vty, "send-lifetime", + argv[idx_hhmmss]->arg, + argv[idx_number]->arg, + argv[idx_month]->arg, + argv[idx_number_2]->arg); +} + +DEFPY_YANG(send_lifetime_duration_day_month, + send_lifetime_duration_day_month_cmd, + "send-lifetime HH:MM:SS (1-31) MONTH (1993-2035) duration (1-2147483646)", + "Set send lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Duration of the key\n" + "Duration seconds\n") +{ + int idx_hhmmss = 1; + int idx_number = 2; + int idx_month = 3; + int idx_number_2 = 4; + int idx_number_3 = 6; + + return key_lifetime_duration_set(vty, "send-lifetime", + argv[idx_hhmmss]->arg, + argv[idx_number]->arg, + argv[idx_month]->arg, + argv[idx_number_2]->arg, + argv[idx_number_3]->arg); +} + +DEFPY_YANG(send_lifetime_duration_month_day, + send_lifetime_duration_month_day_cmd, + "send-lifetime HH:MM:SS MONTH (1-31) (1993-2035) duration (1-2147483646)", + "Set send lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Duration of the key\n" + "Duration seconds\n") +{ + int idx_hhmmss = 1; + int idx_month = 2; + int idx_number = 3; + int idx_number_2 = 4; + int idx_number_3 = 6; + + return key_lifetime_duration_set(vty, "send-lifetime", + argv[idx_hhmmss]->arg, + argv[idx_number]->arg, + argv[idx_month]->arg, + argv[idx_number_2]->arg, + argv[idx_number_3]->arg); +} + +DEFUN (no_send_lifetime, + no_send_lifetime_cmd, + "no send-lifetime", + NO_STR + "Unset send-lifetime\n") +{ + nb_cli_enqueue_change(vty, "send-lifetime", NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain + */ +void key_chains_key_chain_cli_write(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, "key chain %s\n", yang_dnode_get_string(dnode, "name")); +} + +void key_chains_key_chain_cli_write_end(struct vty *vty, + const struct lyd_node *dnode) +{ + vty_out(vty, "exit\n"); + vty_out(vty, "!\n"); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/description + */ +void key_chains_key_chain_description_cli_write(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + /* Implement CLI */ + /* vty_out(vty, " description %s\n", yang_dnode_get_string(dnode)); */ +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key + */ +void key_chains_key_chain_key_cli_write(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " key %s\n", yang_dnode_get_string(dnode, "key-id")); +} + +void key_chains_key_chain_key_cli_write_end(struct vty *vty, + const struct lyd_node *dnode) +{ + vty_out(vty, " exit\n"); +} + +static const char *__dnode_to_key_strftime(char *buf, size_t bufsize, + const struct lyd_node *lt_start_dnode) +{ + const char *timestr; + struct lyd_node *end_node; + struct tm tm; + uint32_t duration; + time_t time; + int len, sz; + char *s; + + s = buf; + sz = bufsize; + + timestr = yang_dnode_get_string(lt_start_dnode, NULL); + (void)ly_time_str2time(timestr, &time, NULL); + localtime_r(&time, &tm); + len = strftime(s, sz, "%T %b %e %Y", &tm); + s += len; + sz -= len; + + if (yang_dnode_exists(lt_start_dnode, "../no-end-time")) { + strlcat(s, " infinite", sz); + return buf; + } + + end_node = yang_dnode_get(lt_start_dnode, "../duration"); + if (end_node) { + duration = yang_dnode_get_uint32(end_node, NULL); + snprintf(s, sz, " duration %u", (uint)duration); + return buf; + } + + timestr = yang_dnode_get_string(lt_start_dnode, "../end-date-time"); + (void)ly_time_str2time(timestr, &time, NULL); + localtime_r(&time, &tm); + strftime(s, sz, " %T %b %e %Y", &tm); + return buf; +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/send-accept-lifetime/start-date-time + */ +void key_chains_key_chain_key_lifetime_send_accept_lifetime_start_date_time_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + char s[256]; + + vty_out(vty, " send-lifetime %s\n", + __dnode_to_key_strftime(s, sizeof(s), dnode)); + vty_out(vty, " accept-lifetime %s\n", s); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/send-lifetime/start-date-time + */ +void key_chains_key_chain_key_lifetime_send_lifetime_start_date_time_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + char s[256]; + + vty_out(vty, " send-lifetime %s\n", + __dnode_to_key_strftime(s, sizeof(s), dnode)); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/accept-lifetime/start-date-time + */ +void key_chains_key_chain_key_lifetime_accept_lifetime_start_date_time_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + char s[256]; + + vty_out(vty, " accept-lifetime %s\n", + __dnode_to_key_strftime(s, sizeof(s), dnode)); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/crypto-algorithm + */ +void key_chains_key_chain_key_crypto_algorithm_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + static const char prefix[] = "ietf-key-chain:"; + static const int prefix_len = sizeof(prefix) - 1; + const char *name = yang_dnode_get_string(dnode, NULL); + + if (!strncmp(name, prefix, prefix_len)) + name += prefix_len; + vty_out(vty, " cryptographic-algorithm %s\n", name); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/key-string/keystring + */ +void key_chains_key_chain_key_key_string_keystring_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + vty_out(vty, " key-string %s\n", yang_dnode_get_string(dnode, NULL)); +} + +static const char * const keychain_features[] = { + "independent-send-accept-lifetime", + NULL, +}; + +/* clang-format off */ +const struct frr_yang_module_info ietf_key_chain_cli_info = { + .name = "ietf-key-chain", + .features = (const char **)keychain_features, + .ignore_cfg_cbs = true, + .nodes = { + { + .xpath = "/ietf-key-chain:key-chains/key-chain", + .cbs = { + .cli_show = key_chains_key_chain_cli_write, + .cli_show_end = key_chains_key_chain_cli_write_end, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/description", + .cbs = { + .cli_show = key_chains_key_chain_description_cli_write, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key", + .cbs = { + .cli_show = key_chains_key_chain_key_cli_write, + .cli_show_end = key_chains_key_chain_key_cli_write_end, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/send-accept-lifetime/start-date-time", + .cbs = { + .cli_show = key_chains_key_chain_key_lifetime_send_accept_lifetime_start_date_time_cli_write, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/send-lifetime/start-date-time", + .cbs = { + .cli_show = key_chains_key_chain_key_lifetime_send_lifetime_start_date_time_cli_write, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/accept-lifetime/start-date-time", + .cbs = { + .cli_show = key_chains_key_chain_key_lifetime_accept_lifetime_start_date_time_cli_write, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/crypto-algorithm", + .cbs = { + .cli_show = key_chains_key_chain_key_crypto_algorithm_cli_write, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/key-string/keystring", + .cbs = { + .cli_show = key_chains_key_chain_key_key_string_keystring_cli_write, + } + }, + { + .xpath = NULL, + }, + } +}; + +static int keychain_config_write(struct vty *vty) +{ + const struct lyd_node *dnode; + int written = 0; + + dnode = yang_dnode_get(running_config->dnode, + "/ietf-key-chain:key-chains"); + if (dnode) { + nb_cli_show_dnode_cmds(vty, dnode, false); + written = 1; + } + return written; +} + +static struct cmd_node keychain_node = { + .name = "keychain", + .node = KEYCHAIN_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-keychain)# ", + .config_write = keychain_config_write, +}; + +static struct cmd_node keychain_key_node = { + .name = "keychain key", + .node = KEYCHAIN_KEY_NODE, + .parent_node = KEYCHAIN_NODE, + .prompt = "%s(config-keychain-key)# ", +}; + +static const struct cmd_variable_handler keychain_var_handlers[] = { + {.varname = "key_chain", .xpath = "/ietf-key-chain:key-chains/key-chain/name" }, + {.tokenname = "KEYCHAIN_NAME", .xpath = "/ietf-key-chain:key-chains/key-chain/name" }, + {.completions = NULL} +}; + +void keychain_cli_init(void) +{ + /* Register handler for keychain auto config support */ + cmd_variable_handler_register(keychain_var_handlers); + install_node(&keychain_node); + install_node(&keychain_key_node); + + install_default(KEYCHAIN_NODE); + install_default(KEYCHAIN_KEY_NODE); + + install_element(CONFIG_NODE, &key_chain_cmd); + install_element(CONFIG_NODE, &no_key_chain_cmd); + install_element(KEYCHAIN_NODE, &key_cmd); + install_element(KEYCHAIN_NODE, &no_key_cmd); + + install_element(KEYCHAIN_NODE, &key_chain_cmd); + install_element(KEYCHAIN_NODE, &no_key_chain_cmd); + + install_element(KEYCHAIN_KEY_NODE, &key_string_cmd); + install_element(KEYCHAIN_KEY_NODE, &no_key_string_cmd); + + install_element(KEYCHAIN_KEY_NODE, &key_chain_cmd); + install_element(KEYCHAIN_KEY_NODE, &no_key_chain_cmd); + + install_element(KEYCHAIN_KEY_NODE, &key_cmd); + install_element(KEYCHAIN_KEY_NODE, &no_key_cmd); + + install_element(KEYCHAIN_KEY_NODE, + &accept_lifetime_day_month_day_month_cmd); + install_element(KEYCHAIN_KEY_NODE, + &accept_lifetime_day_month_month_day_cmd); + install_element(KEYCHAIN_KEY_NODE, + &accept_lifetime_month_day_day_month_cmd); + install_element(KEYCHAIN_KEY_NODE, + &accept_lifetime_month_day_month_day_cmd); + install_element(KEYCHAIN_KEY_NODE, + &accept_lifetime_infinite_day_month_cmd); + install_element(KEYCHAIN_KEY_NODE, + &accept_lifetime_infinite_month_day_cmd); + install_element(KEYCHAIN_KEY_NODE, + &accept_lifetime_duration_day_month_cmd); + install_element(KEYCHAIN_KEY_NODE, + &accept_lifetime_duration_month_day_cmd); + install_element(KEYCHAIN_KEY_NODE, &no_accept_lifetime_cmd); + + install_element(KEYCHAIN_KEY_NODE, + &send_lifetime_day_month_day_month_cmd); + install_element(KEYCHAIN_KEY_NODE, + &send_lifetime_day_month_month_day_cmd); + install_element(KEYCHAIN_KEY_NODE, + &send_lifetime_month_day_day_month_cmd); + install_element(KEYCHAIN_KEY_NODE, + &send_lifetime_month_day_month_day_cmd); + install_element(KEYCHAIN_KEY_NODE, + &send_lifetime_infinite_day_month_cmd); + install_element(KEYCHAIN_KEY_NODE, + &send_lifetime_infinite_month_day_cmd); + install_element(KEYCHAIN_KEY_NODE, + &send_lifetime_duration_day_month_cmd); + install_element(KEYCHAIN_KEY_NODE, + &send_lifetime_duration_month_day_cmd); + install_element(KEYCHAIN_KEY_NODE, &no_send_lifetime_cmd); + install_element(KEYCHAIN_KEY_NODE, &cryptographic_algorithm_cmd); + install_element(KEYCHAIN_KEY_NODE, &no_cryptographic_algorithm_cmd); +} diff --git a/lib/keychain_nb.c b/lib/keychain_nb.c new file mode 100644 index 0000000..57967b3 --- /dev/null +++ b/lib/keychain_nb.c @@ -0,0 +1,898 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * February 22 2024, Christian Hopps <chopps@labn.net> + * + * Copyright (C) 2024 LabN Consulting, L.L.C. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <zebra.h> +#include "lib_errors.h" +#include "northbound.h" +#include "keychain.h" + +static void keychain_touch(struct keychain *keychain) +{ + keychain->last_touch = time(NULL); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain + */ +static int key_chains_key_chain_create(struct nb_cb_create_args *args) +{ + const char *name; + struct keychain *keychain; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + name = yang_dnode_get_string(args->dnode, "name"); + keychain = keychain_get(name); + keychain_touch(keychain); + return NB_OK; +} + +static int key_chains_key_chain_destroy(struct nb_cb_destroy_args *args) +{ + const char *name; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + name = yang_dnode_get_string(args->dnode, "name"); + keychain_delete(keychain_lookup(name)); + return NB_OK; +} + +static const void *key_chains_key_chain_get_next(struct nb_cb_get_next_args *args) +{ + const struct listnode *prev = args->list_entry; + + return prev ? prev->next : keychain_list->head; +} + +static int key_chains_key_chain_get_keys(struct nb_cb_get_keys_args *args) +{ + const struct listnode *node = args->list_entry; + const struct keychain *keychain = node->data; + + args->keys->num = 1; + strlcpy(args->keys->key[0], keychain->name, sizeof(args->keys->key[0])); + return NB_OK; +} + +static const void *key_chains_key_chain_lookup_entry(struct nb_cb_lookup_entry_args *args) +{ + const char *name = args->keys->key[0]; + struct keychain *keychain; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(keychain_list, node, keychain)) { + if (strcmp(keychain->name, name) == 0) + return node; + } + return NULL; +} + + +static int __destroy_nop(struct nb_cb_destroy_args *args) +{ + /* modified by sibling or cleaned up by container destroy */ + return NB_OK; +} + +static struct key *__dnode_get_key2(const struct lyd_node *dnode, bool touch) +{ + struct keychain *keychain; + const char *name; + struct key *key; + uint32_t index; + + name = yang_dnode_get_string(dnode, "../../../name"); + keychain = keychain_lookup(name); + index = (uint32_t)yang_dnode_get_uint64(dnode, "../../key-id"); + key = key_lookup(keychain, index); + if (touch) + keychain_touch(keychain); + return key; +} + +static struct key *__dnode_get_key3(const struct lyd_node *dnode, bool touch) +{ + struct keychain *keychain; + const char *name; + struct key *key; + uint32_t index; + + name = yang_dnode_get_string(dnode, "../../../../name"); + keychain = keychain_lookup(name); + index = (uint32_t)yang_dnode_get_uint64(dnode, "../../../key-id"); + key = key_lookup(keychain, index); + if (touch) + keychain_touch(keychain); + return key; +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/description + */ +static int key_chains_key_chain_description_modify(struct nb_cb_modify_args *args) +{ + struct keychain *keychain; + const char *name; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + name = yang_dnode_get_string(args->dnode, "../name"); + keychain = keychain_lookup(name); + XFREE(MTYPE_KEYCHAIN_DESC, keychain->desc); + keychain->desc = XSTRDUP(MTYPE_KEYCHAIN_DESC, + yang_dnode_get_string(args->dnode, NULL)); + + keychain_touch(keychain); + return NB_OK; +} + +static int key_chains_key_chain_description_destroy(struct nb_cb_destroy_args *args) +{ + struct keychain *keychain; + const char *name; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + name = yang_dnode_get_string(args->dnode, "../name"); + keychain = keychain_lookup(name); + XFREE(MTYPE_KEYCHAIN_DESC, keychain->desc); + + keychain_touch(keychain); + return NB_OK; +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/last-modified-timestamp + */ +static struct yang_data *key_chains_key_chain_last_modified_timestamp_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct listnode *kcnode = args->list_entry; + const struct keychain *keychain = kcnode->data; + + return yang_data_new_date_and_time(args->xpath, keychain->last_touch, + false); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key + */ +static int key_chains_key_chain_key_create(struct nb_cb_create_args *args) +{ + struct keychain *keychain; + struct key *key; + const char *name; + uint64_t keyid; + + if (args->event != NB_EV_VALIDATE && args->event != NB_EV_APPLY) + return NB_OK; + + keyid = yang_dnode_get_uint64(args->dnode, "key-id"); + if (args->event == NB_EV_VALIDATE) { + if (keyid > UINT32_MAX) { + /* Warn most protocols can't use this value */ + flog_err(EC_LIB_NB_CB_CONFIG_VALIDATE, + "Protocols do not accept > 32-bit key-id values"); + return NB_EV_VALIDATE; + } + return NB_OK; + } + if (args->event != NB_EV_APPLY) + return NB_OK; + + name = yang_dnode_get_string(args->dnode, "../name"); + keychain = keychain_lookup(name); + assert(keyid <= UINT32_MAX); + key = key_get(keychain, (uint32_t)keyid); + assert(key); + + keychain_touch(keychain); + return NB_OK; +} + +static int key_chains_key_chain_key_destroy(struct nb_cb_destroy_args *args) +{ + struct keychain *keychain; + struct key *key; + const char *name; + uint64_t keyid; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + keyid = yang_dnode_get_uint64(args->dnode, "key-id"); + if (keyid > UINT32_MAX) + return NB_ERR_NOT_FOUND; + name = yang_dnode_get_string(args->dnode, "../name"); + keychain = keychain_lookup(name); + key = key_lookup(keychain, (uint32_t)keyid); + key_delete(keychain, key); + + keychain_touch(keychain); + return NB_OK; +} + +static const void *key_chains_key_chain_key_get_next(struct nb_cb_get_next_args *args) +{ + const struct listnode *kcnode = args->parent_list_entry; + const struct keychain *keychain = kcnode->data; + const struct listnode *prev = args->list_entry; + + return prev ? prev->next : keychain->key->head; +} + +static int key_chains_key_chain_key_get_keys(struct nb_cb_get_keys_args *args) +{ + const struct listnode *node = args->list_entry; + const struct key *key = node->data; + + args->keys->num = 1; + snprintf(args->keys->key[0], sizeof(args->keys->key[0]), "%" PRIu32, + key->index); + + return NB_OK; +} + +static const void *key_chains_key_chain_key_lookup_entry(struct nb_cb_lookup_entry_args *args) +{ + const struct listnode *kcnode = args->parent_list_entry; + const struct keychain *keychain = kcnode->data; + struct listnode *node; + struct key *key; + uint32_t index; + + index = strtoul(args->keys->key[0], NULL, 0); + for (ALL_LIST_ELEMENTS_RO(keychain->key, node, key)) + if (key->index == index) + return node; + return NULL; +} + +static int __lifetime_create(struct nb_cb_create_args *args, bool send, + bool accept, bool always) +{ + struct key *key; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + if (always) + key = __dnode_get_key3(args->dnode, true); + else + key = __dnode_get_key2(args->dnode, true); + if (send) { + key->send.start = 0; + key->send.end = -1; + key->send.duration = 0; + } + if (accept) { + key->accept.start = 0; + key->accept.end = -1; + key->accept.duration = 0; + } + return NB_OK; +} + +static int __lifetime_start_date_time_modify(struct nb_cb_modify_args *args, + bool send, bool accept) +{ + struct key *key; + time_t time; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + key = __dnode_get_key3(args->dnode, true); + time = yang_dnode_get_date_and_time(args->dnode, NULL); + + if (send) + key->send.start = time; + if (accept) + key->accept.start = time; + + return NB_OK; +} + +static int __lifetime_no_end_time_create(struct nb_cb_create_args *args, + bool send, bool accept) +{ + struct key *key; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + key = __dnode_get_key3(args->dnode, true); + if (send) + key->send.end = -1; + if (accept) + key->accept.end = -1; + return NB_OK; +} + +static int __lifetime_duration_modify(struct nb_cb_modify_args *args, bool send, + bool accept) +{ + struct key *key; + uint32_t duration; + time_t time; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + key = __dnode_get_key3(args->dnode, true); + time = yang_dnode_get_date_and_time(args->dnode, "../start-date-time"); + duration = yang_dnode_get_uint32(args->dnode, NULL); + + if (send) + key->send.end = time + duration; + if (accept) + key->accept.end = time + duration; + return NB_OK; +} + +static int __lifetime_end_date_time_modify(struct nb_cb_modify_args *args, + bool send, bool accept) +{ + struct key *key; + time_t time; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + key = __dnode_get_key3(args->dnode, true); + time = yang_dnode_get_date_and_time(args->dnode, NULL); + + if (send) + key->send.end = time; + if (accept) + key->accept.end = time; + return NB_OK; +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/send-accept-lifetime + */ +static int key_chains_key_chain_key_lifetime_send_accept_lifetime_create( + struct nb_cb_create_args *args) +{ + + return __lifetime_create(args, true, true, false); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/send-accept-lifetime/always + */ +static int key_chains_key_chain_key_lifetime_send_accept_lifetime_always_create( + struct nb_cb_create_args *args) +{ + return __lifetime_create(args, true, true, true); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/send-accept-lifetime/start-date-time + */ +static int +key_chains_key_chain_key_lifetime_send_accept_lifetime_start_date_time_modify( + struct nb_cb_modify_args *args) +{ + return __lifetime_start_date_time_modify(args, true, true); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/send-accept-lifetime/no-end-time + */ +static int +key_chains_key_chain_key_lifetime_send_accept_lifetime_no_end_time_create( + struct nb_cb_create_args *args) +{ + return __lifetime_no_end_time_create(args, true, true); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/send-accept-lifetime/duration + */ +static int key_chains_key_chain_key_lifetime_send_accept_lifetime_duration_modify( + struct nb_cb_modify_args *args) +{ + return __lifetime_duration_modify(args, true, true); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/send-accept-lifetime/end-date-time + */ +static int +key_chains_key_chain_key_lifetime_send_accept_lifetime_end_date_time_modify( + struct nb_cb_modify_args *args) +{ + return __lifetime_end_date_time_modify(args, true, true); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/send-lifetime + */ +static int key_chains_key_chain_key_lifetime_send_lifetime_create( + struct nb_cb_create_args *args) +{ + + return __lifetime_create(args, true, false, false); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/send-lifetime/always + */ +static int key_chains_key_chain_key_lifetime_send_lifetime_always_create( + struct nb_cb_create_args *args) +{ + return __lifetime_create(args, true, false, true); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/send-lifetime/start-date-time + */ +static int key_chains_key_chain_key_lifetime_send_lifetime_start_date_time_modify(struct nb_cb_modify_args *args) +{ + return __lifetime_start_date_time_modify(args, true, false); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/send-lifetime/no-end-time + */ +static int key_chains_key_chain_key_lifetime_send_lifetime_no_end_time_create(struct nb_cb_create_args *args) +{ + return __lifetime_no_end_time_create(args, true, false); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/send-lifetime/duration + */ +static int key_chains_key_chain_key_lifetime_send_lifetime_duration_modify(struct nb_cb_modify_args *args) +{ + return __lifetime_duration_modify(args, true, false); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/send-lifetime/end-date-time + */ +static int key_chains_key_chain_key_lifetime_send_lifetime_end_date_time_modify(struct nb_cb_modify_args *args) +{ + return __lifetime_end_date_time_modify(args, true, false); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/accept-lifetime + */ +static int key_chains_key_chain_key_lifetime_accept_lifetime_create( + struct nb_cb_create_args *args) +{ + + return __lifetime_create(args, false, true, false); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/accept-lifetime/always + */ +static int key_chains_key_chain_key_lifetime_accept_lifetime_always_create(struct nb_cb_create_args *args) +{ + return __lifetime_create(args, false, true, true); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/accept-lifetime/start-date-time + */ +static int key_chains_key_chain_key_lifetime_accept_lifetime_start_date_time_modify(struct nb_cb_modify_args *args) +{ + return __lifetime_start_date_time_modify(args, false, true); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/accept-lifetime/no-end-time + */ +static int key_chains_key_chain_key_lifetime_accept_lifetime_no_end_time_create(struct nb_cb_create_args *args) +{ + return __lifetime_no_end_time_create(args, false, true); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/accept-lifetime/duration + */ +static int key_chains_key_chain_key_lifetime_accept_lifetime_duration_modify(struct nb_cb_modify_args *args) +{ + return __lifetime_duration_modify(args, false, true); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/lifetime/accept-lifetime/end-date-time + */ +static int key_chains_key_chain_key_lifetime_accept_lifetime_end_date_time_modify(struct nb_cb_modify_args *args) +{ + return __lifetime_end_date_time_modify(args, false, true); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/crypto-algorithm + */ +static int key_chains_key_chain_key_crypto_algorithm_modify(struct nb_cb_modify_args *args) +{ + static const char prefix[] = "ietf-key-chain:"; + static const int prefix_len = sizeof(prefix) - 1; + struct keychain *keychain; + const char *name; + struct key *key; + uint32_t index; + uint8_t hash_algo; + + if (args->event != NB_EV_VALIDATE && args->event != NB_EV_APPLY) + return NB_OK; + + name = yang_dnode_get_string(args->dnode, NULL); + if (!strncmp(name, prefix, prefix_len)) + name += prefix_len; + hash_algo = keychain_get_algo_id_by_name(name); + + if (args->event == NB_EV_VALIDATE) { + if (!hash_algo) { + zlog_err("\"%s\" hash algo not supported", name); + return NB_ERR_VALIDATION; + } +#ifndef CRYPTO_OPENSSL + if (hash_algo == KEYCHAIN_ALGO_NULL) { + zlog_err("\"%s\" algo not supported, compile with --with-crypto=openssl", + name); + return NB_ERR_VALIDATION; + } +#endif /* CRYPTO_OPENSSL */ + return NB_OK; + } + + assert(args->event == NB_EV_APPLY); + name = yang_dnode_get_string(args->dnode, "../../name"); + keychain = keychain_lookup(name); + index = (uint32_t)yang_dnode_get_uint64(args->dnode, "../key-id"); + key = key_lookup(keychain, index); + key->hash_algo = hash_algo; + + keychain_touch(keychain); + return NB_OK; +} + +static int key_chains_key_chain_key_crypto_algorithm_destroy( + struct nb_cb_destroy_args *args) +{ + struct keychain *keychain; + const char *name; + struct key *key; + uint32_t index; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + name = yang_dnode_get_string(args->dnode, "../../../name"); + keychain = keychain_lookup(name); + 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); + + return NB_OK; +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/key-string/keystring + */ +static int key_chains_key_chain_key_key_string_keystring_modify(struct nb_cb_modify_args *args) +{ + struct keychain *keychain; + const char *name; + struct key *key; + uint32_t index; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + name = yang_dnode_get_string(args->dnode, "../../../name"); + keychain = keychain_lookup(name); + index = (uint32_t)yang_dnode_get_uint64(args->dnode, "../../key-id"); + key = key_lookup(keychain, index); + assert(key); + + + if (key->string) + XFREE(MTYPE_KEY, key->string); + key->string = XSTRDUP(MTYPE_KEY, + yang_dnode_get_string(args->dnode, NULL)); + + keychain_touch(keychain); + return NB_OK; +} + +static int key_chains_key_chain_key_key_string_keystring_destroy(struct nb_cb_destroy_args *args) +{ + struct keychain *keychain; + const char *name; + struct key *key; + uint32_t index; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + name = yang_dnode_get_string(args->dnode, "../../../name"); + keychain = keychain_lookup(name); + index = (uint32_t)yang_dnode_get_uint64(args->dnode, "../../key-id"); + key = key_lookup(keychain, index); + assert(key); + + XFREE(MTYPE_KEY, key->string); + keychain_touch(keychain); + + return NB_OK; +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/send-lifetime-active + */ +static struct yang_data *key_chains_key_chain_key_send_lifetime_active_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct listnode *node = args->list_entry; + const struct key *key = node->data; + time_t now = time(NULL); + bool active = false; + + if (key->send.start == 0) + active = true; + else if (key->send.start <= now) + if (key->send.end >= now || key->send.end == -1) + active = true; + + return yang_data_new_bool(args->xpath, active); +} + +/* + * XPath: /ietf-key-chain:key-chains/key-chain/key/accept-lifetime-active + */ +static struct yang_data *key_chains_key_chain_key_accept_lifetime_active_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct listnode *node = args->list_entry; + const struct key *key = node->data; + time_t now = time(NULL); + bool active = false; + + if (key->accept.start == 0) + active = true; + else if (key->accept.start <= now) + if (key->accept.end >= now || key->accept.end == -1) + active = true; + + return yang_data_new_bool(args->xpath, active); +} + +static const char * const keychain_features[] = { + "independent-send-accept-lifetime", + NULL, +}; + +/* clang-format off */ +const struct frr_yang_module_info ietf_key_chain_info = { + .name = "ietf-key-chain", + .features = (const char **)keychain_features, + .nodes = { + { + .xpath = "/ietf-key-chain:key-chains/key-chain", + .cbs = { + .create = key_chains_key_chain_create, + .destroy = key_chains_key_chain_destroy, + .get_next = key_chains_key_chain_get_next, + .get_keys = key_chains_key_chain_get_keys, + .lookup_entry = key_chains_key_chain_lookup_entry, + .cli_show = key_chains_key_chain_cli_write, + .cli_show_end = key_chains_key_chain_cli_write_end, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/description", + .cbs = { + .modify = key_chains_key_chain_description_modify, + .destroy = key_chains_key_chain_description_destroy, + .cli_show = key_chains_key_chain_description_cli_write, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/last-modified-timestamp", + .cbs = { + .get_elem = key_chains_key_chain_last_modified_timestamp_get_elem, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key", + .cbs = { + .create = key_chains_key_chain_key_create, + .destroy = key_chains_key_chain_key_destroy, + .get_next = key_chains_key_chain_key_get_next, + .get_keys = key_chains_key_chain_key_get_keys, + .lookup_entry = key_chains_key_chain_key_lookup_entry, + .cli_show = key_chains_key_chain_key_cli_write, + .cli_show_end = key_chains_key_chain_key_cli_write_end, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/send-accept-lifetime", + .cbs = { + .create = key_chains_key_chain_key_lifetime_send_accept_lifetime_create, + .destroy = __destroy_nop, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/send-accept-lifetime/always", + .cbs = { + .create = key_chains_key_chain_key_lifetime_send_accept_lifetime_always_create, + .destroy = __destroy_nop, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/send-accept-lifetime/start-date-time", + .cbs = { + .modify = key_chains_key_chain_key_lifetime_send_accept_lifetime_start_date_time_modify, + .destroy = __destroy_nop, + .cli_show = key_chains_key_chain_key_lifetime_send_accept_lifetime_start_date_time_cli_write, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/send-accept-lifetime/no-end-time", + .cbs = { + .create = key_chains_key_chain_key_lifetime_send_accept_lifetime_no_end_time_create, + .destroy = __destroy_nop, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/send-accept-lifetime/duration", + .cbs = { + .modify = key_chains_key_chain_key_lifetime_send_accept_lifetime_duration_modify, + .destroy = __destroy_nop, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/send-accept-lifetime/end-date-time", + .cbs = { + .modify = key_chains_key_chain_key_lifetime_send_accept_lifetime_end_date_time_modify, + .destroy = __destroy_nop, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/send-lifetime", + .cbs = { + .create = key_chains_key_chain_key_lifetime_send_lifetime_create, + .destroy = __destroy_nop, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/send-lifetime/always", + .cbs = { + .create = key_chains_key_chain_key_lifetime_send_lifetime_always_create, + .destroy = __destroy_nop, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/send-lifetime/start-date-time", + .cbs = { + .modify = key_chains_key_chain_key_lifetime_send_lifetime_start_date_time_modify, + .destroy = __destroy_nop, + .cli_show = key_chains_key_chain_key_lifetime_send_lifetime_start_date_time_cli_write, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/send-lifetime/no-end-time", + .cbs = { + .create = key_chains_key_chain_key_lifetime_send_lifetime_no_end_time_create, + .destroy = __destroy_nop, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/send-lifetime/duration", + .cbs = { + .modify = key_chains_key_chain_key_lifetime_send_lifetime_duration_modify, + .destroy = __destroy_nop, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/send-lifetime/end-date-time", + .cbs = { + .modify = key_chains_key_chain_key_lifetime_send_lifetime_end_date_time_modify, + .destroy = __destroy_nop, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/accept-lifetime", + .cbs = { + .create = key_chains_key_chain_key_lifetime_accept_lifetime_create, + .destroy = __destroy_nop, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/accept-lifetime/always", + .cbs = { + .create = key_chains_key_chain_key_lifetime_accept_lifetime_always_create, + .destroy = __destroy_nop, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/accept-lifetime/start-date-time", + .cbs = { + .modify = key_chains_key_chain_key_lifetime_accept_lifetime_start_date_time_modify, + .destroy = __destroy_nop, + .cli_show = key_chains_key_chain_key_lifetime_accept_lifetime_start_date_time_cli_write, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/accept-lifetime/no-end-time", + .cbs = { + .create = key_chains_key_chain_key_lifetime_accept_lifetime_no_end_time_create, + .destroy = __destroy_nop, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/accept-lifetime/duration", + .cbs = { + .modify = key_chains_key_chain_key_lifetime_accept_lifetime_duration_modify, + .destroy = __destroy_nop, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/lifetime/accept-lifetime/end-date-time", + .cbs = { + .modify = key_chains_key_chain_key_lifetime_accept_lifetime_end_date_time_modify, + .destroy = __destroy_nop, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/crypto-algorithm", + .cbs = { + .modify = key_chains_key_chain_key_crypto_algorithm_modify, + .destroy = key_chains_key_chain_key_crypto_algorithm_destroy, + .cli_show = key_chains_key_chain_key_crypto_algorithm_cli_write, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/key-string/keystring", + .cbs = { + .modify = key_chains_key_chain_key_key_string_keystring_modify, + .destroy = key_chains_key_chain_key_key_string_keystring_destroy, + .cli_show = key_chains_key_chain_key_key_string_keystring_cli_write, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/send-lifetime-active", + .cbs = { + .get_elem = key_chains_key_chain_key_send_lifetime_active_get_elem, + } + }, + { + .xpath = "/ietf-key-chain:key-chains/key-chain/key/accept-lifetime-active", + .cbs = { + .get_elem = key_chains_key_chain_key_accept_lifetime_active_get_elem, + } + }, + { + .xpath = NULL, + }, + }, +}; diff --git a/lib/lib_errors.c b/lib/lib_errors.c index a96fac9..9d6c043 100644 --- a/lib/lib_errors.c +++ b/lib/lib_errors.c @@ -308,24 +308,6 @@ static struct log_ref ferr_lib_err[] = { .suggestion = "Check if the FRR libyang plugins were installed correctly in the system", }, { - .code = EC_LIB_CONFD_INIT, - .title = "ConfD initialization error", - .description = "Upon startup FRR failed to properly initialize and startup the ConfD northbound plugin", - .suggestion = "Check if ConfD is installed correctly in the system. Also, check if the confd daemon is running.", - }, - { - .code = EC_LIB_CONFD_DATA_CONVERT, - .title = "ConfD data conversion error", - .description = "An error has occurred while converting a ConfD data value (binary) to a string", - .suggestion = "Open an Issue with all relevant log files and restart FRR" - }, - { - .code = EC_LIB_LIBCONFD, - .title = "libconfd error", - .description = "The northbound subsystem has detected that the libconfd library returned an error", - .suggestion = "Open an Issue with all relevant log files and restart FRR" - }, - { .code = EC_LIB_SYSREPO_INIT, .title = "Sysrepo initialization error", .description = "Upon startup FRR failed to properly initialize and startup the Sysrepo northbound plugin", diff --git a/lib/lib_errors.h b/lib/lib_errors.h index 8cdfb16..9e0d539 100644 --- a/lib/lib_errors.h +++ b/lib/lib_errors.h @@ -65,9 +65,6 @@ enum lib_log_refs { EC_LIB_NB_TRANSACTION_RECORD_FAILED, EC_LIB_LIBYANG, EC_LIB_LIBYANG_PLUGIN_LOAD, - EC_LIB_CONFD_INIT, - EC_LIB_CONFD_DATA_CONVERT, - EC_LIB_LIBCONFD, EC_LIB_SYSREPO_INIT, EC_LIB_SYSREPO_DATA_CONVERT, EC_LIB_LIBSYSREPO, diff --git a/lib/libagentx.c b/lib/libagentx.c new file mode 100644 index 0000000..2382657 --- /dev/null +++ b/lib/libagentx.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* SNMP cli support + * Copyright (C) 2024 Donald Sharp <sharpd@nvidia.com> NVIDIA Corporation + */ +#include <zebra.h> + +#include "lib/hook.h" +#include "lib/libagentx.h" +#include "command.h" + +DEFINE_HOOK(agentx_cli_enabled, (), ()); +DEFINE_HOOK(agentx_cli_disabled, (), ()); + +bool agentx_enabled; + +/* AgentX node. */ +static int config_write_agentx(struct vty *vty) +{ + if (agentx_enabled) + vty_out(vty, "agentx\n"); + return 1; +} + +static struct cmd_node agentx_node = { + .name = "smux", + .node = SMUX_NODE, + .prompt = "", + .config_write = config_write_agentx, +}; + +DEFUN(agentx_enable, agentx_enable_cmd, "agentx", + "SNMP AgentX protocol settings\n") +{ + if (!hook_have_hooks(agentx_cli_enabled)) { + zlog_info( + "agentx specified but the agentx Module is not loaded, is this intentional?"); + + return CMD_SUCCESS; + } + + hook_call(agentx_cli_enabled); + + return CMD_SUCCESS; +} + +DEFUN(no_agentx, no_agentx_cmd, "no agentx", + NO_STR "SNMP AgentX protocol settings\n") +{ + vty_out(vty, "SNMP AgentX support cannot be disabled once enabled\n"); + if (!hook_call(agentx_cli_disabled)) + return CMD_WARNING_CONFIG_FAILED; + + return CMD_SUCCESS; +} + +void libagentx_init(void) +{ + agentx_enabled = false; + + install_node(&agentx_node); + install_element(CONFIG_NODE, &agentx_enable_cmd); + install_element(CONFIG_NODE, &no_agentx_cmd); +} diff --git a/lib/libagentx.h b/lib/libagentx.h new file mode 100644 index 0000000..c3246d9 --- /dev/null +++ b/lib/libagentx.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* SNMP cli support + * Copyright (C) 2024 Donald Sharp <sharpd@nvidia.com> NVIDIA Corporation + */ +#ifndef __LIBAGENTX_H__ +#define __LIBAGENTX_H__ + +extern void libagentx_init(void); +extern bool agentx_enabled; + +DECLARE_HOOK(agentx_cli_enabled, (), ()); +DECLARE_HOOK(agentx_cli_disabled, (), ()); + +#endif diff --git a/lib/libfrr.c b/lib/libfrr.c index 2861ebe..cc60cfb 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -59,7 +59,7 @@ char config_default[512]; char frr_zclientpath[512]; static char pidfile_default[1024]; #ifdef HAVE_SQLITE3 -static char dbfile_default[512]; +static char dbfile_default[1024]; #endif static char vtypath_default[512]; @@ -319,7 +319,12 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) char *p = strrchr(argv[0], '/'); di->progname = p ? p + 1 : argv[0]; - umask(0027); + if (!getenv("GCOV_PREFIX")) + umask(0027); + else { + /* If we are profiling use a more generous umask */ + umask(0002); + } log_args_init(daemon->early_logging); @@ -1035,7 +1040,17 @@ void frr_config_fork(void) zlog_tls_buffer_init(); } -void frr_vty_serv_start(void) +static void frr_check_detach(void) +{ + if (nodetach_term || nodetach_daemon) + return; + + if (daemon_ctl_sock != -1) + close(daemon_ctl_sock); + daemon_ctl_sock = -1; +} + +void frr_vty_serv_start(bool check_detach) { /* allow explicit override of vty_path in the future * (not currently set anywhere) */ @@ -1058,6 +1073,9 @@ void frr_vty_serv_start(void) } vty_serv_start(di->vty_addr, di->vty_port, di->vty_path); + + if (check_detach) + frr_check_detach(); } void frr_vty_serv_stop(void) @@ -1068,16 +1086,6 @@ void frr_vty_serv_stop(void) unlink(di->vty_path); } -static void frr_check_detach(void) -{ - if (nodetach_term || nodetach_daemon) - return; - - if (daemon_ctl_sock != -1) - close(daemon_ctl_sock); - daemon_ctl_sock = -1; -} - static void frr_terminal_close(int isexit) { int nullfd; @@ -1163,7 +1171,7 @@ void frr_run(struct event_loop *master) char instanceinfo[64] = ""; if (!(di->flags & FRR_MANUAL_VTY_START)) - frr_vty_serv_start(); + frr_vty_serv_start(false); if (di->instance) snprintf(instanceinfo, sizeof(instanceinfo), "instance %u ", @@ -1201,7 +1209,8 @@ void frr_run(struct event_loop *master) close(nullfd); } - frr_check_detach(); + if (!(di->flags & FRR_MANUAL_VTY_START)) + frr_check_detach(); } /* end fixed stderr startup logging */ @@ -1445,3 +1454,12 @@ void _libfrr_version(void) write(1, banner, sizeof(banner) - 1); _exit(0); } + +/* Render simple version tuple to string */ +const char *frr_vers2str(uint32_t version, char *buf, int buflen) +{ + snprintf(buf, buflen, "%d.%d.%d", MAJOR_FRRVERSION(version), + MINOR_FRRVERSION(version), SUB_FRRVERSION(version)); + + return buf; +} diff --git a/lib/libfrr.h b/lib/libfrr.h index ee436d9..d52ee9a 100644 --- a/lib/libfrr.h +++ b/lib/libfrr.h @@ -200,7 +200,7 @@ extern void frr_config_fork(void); extern void frr_run(struct event_loop *master); extern void frr_detach(void); -extern void frr_vty_serv_start(void); +extern void frr_vty_serv_start(bool check_detach); extern void frr_vty_serv_stop(void); extern bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len, @@ -233,6 +233,17 @@ extern bool frr_is_after_fork; extern bool debug_memstats_at_exit; +/* + * Version numbering: MAJOR (8) | MINOR (16) | SUB (8) + */ +#define MAKE_FRRVERSION(maj, min, sub) \ + ((((maj) & 0xff) << 24) | (((min) & 0xffff) << 8) | ((sub) & 0xff)) +#define MAJOR_FRRVERSION(v) (((v) >> 24) & 0xff) +#define MINOR_FRRVERSION(v) (((v) >> 8) & 0xffff) +#define SUB_FRRVERSION(v) ((v) & 0xff) + +const char *frr_vers2str(uint32_t version, char *buf, int buflen); + #ifdef __cplusplus } #endif diff --git a/lib/libfrr_trace.c b/lib/libfrr_trace.c index 5932032..14f4a3c 100644 --- a/lib/libfrr_trace.c +++ b/lib/libfrr_trace.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + #define TRACEPOINT_CREATE_PROBES #define TRACEPOINT_DEFINE diff --git a/lib/libospf.h b/lib/libospf.h index 45e7fb1..0ac490a 100644 --- a/lib/libospf.h +++ b/lib/libospf.h @@ -69,6 +69,7 @@ extern "C" { #define OSPF_MTU_IGNORE_DEFAULT 0 #define OSPF_FAST_HELLO_DEFAULT 0 #define OSPF_P2MP_DELAY_REFLOOD_DEFAULT false +#define OSPF_P2MP_NON_BROADCAST_DEFAULT false #define OSPF_OPAQUE_CAPABLE_DEFAULT true #define OSPF_PREFIX_SUPPRESSION_DEFAULT false #define OSPF_AREA_BACKBONE 0x00000000 /* 0.0.0.0 */ diff --git a/lib/link_state.c b/lib/link_state.c index 25373bd..c758b7f 100644 --- a/lib/link_state.c +++ b/lib/link_state.c @@ -140,6 +140,12 @@ int ls_node_same(struct ls_node *n1, struct ls_node *n2) if (CHECK_FLAG(n1->flags, LS_NODE_MSD) && (n1->msd != n2->msd)) return 0; } + if (CHECK_FLAG(n1->flags, LS_NODE_SRV6)) { + if (n1->srv6_cap_flags != n2->srv6_cap_flags) + return 0; + if (memcmp(&n1->srv6_msd, &n2->srv6_msd, sizeof(n1->srv6_msd))) + return 0; + } /* OK, n1 & n2 are equal */ return 1; @@ -320,6 +326,23 @@ int ls_attributes_same(struct ls_attributes *l1, struct ls_attributes *l2) &l2->adj_sid[i].neighbor.addr))) return 0; } + for (int i = 0; i < ADJ_SRV6_MAX; i++) { + if (!CHECK_FLAG(l1->flags, (LS_ATTR_ADJ_SRV6SID << i))) + continue; + if (memcmp(&l1->adj_srv6_sid[i].sid, &l2->adj_srv6_sid[i].sid, + sizeof(struct in6_addr)) || + (l1->adj_srv6_sid[i].flags != l2->adj_srv6_sid[i].flags) || + (l1->adj_srv6_sid[i].weight != l2->adj_srv6_sid[i].weight) || + (l1->adj_srv6_sid[i].endpoint_behavior != + l2->adj_srv6_sid[i].endpoint_behavior)) + return 0; + if (((l1->adv.origin == ISIS_L1) || + (l1->adv.origin == ISIS_L2)) && + (memcmp(&l1->adj_srv6_sid[i].neighbor.sysid, + &l2->adj_srv6_sid[i].neighbor.sysid, + ISO_SYS_ID_LEN) != 0)) + return 0; + } if (CHECK_FLAG(l1->flags, LS_ATTR_SRLG) && ((l1->srlg_len != l2->srlg_len) || memcmp(l1->srlgs, l2->srlgs, @@ -1298,6 +1321,26 @@ static struct ls_attributes *ls_parse_attributes(struct stream *s) STREAM_GET(attr->adj_sid[ADJ_BCK_IPV6].neighbor.sysid, s, ISO_SYS_ID_LEN); } + if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SRV6SID)) { + STREAM_GET(&attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].sid, s, + sizeof(struct in6_addr)); + STREAM_GETC(s, attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].flags); + STREAM_GETC(s, attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].weight); + STREAM_GETW(s, attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6] + .endpoint_behavior); + STREAM_GET(attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].neighbor.sysid, + s, ISO_SYS_ID_LEN); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SRV6SID)) { + STREAM_GET(&attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].sid, s, + sizeof(struct in6_addr)); + STREAM_GETC(s, attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].flags); + STREAM_GETC(s, attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].weight); + STREAM_GETW(s, attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6] + .endpoint_behavior); + STREAM_GET(attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].neighbor.sysid, + s, ISO_SYS_ID_LEN); + } if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) { STREAM_GETC(s, len); attr->srlgs = XCALLOC(MTYPE_LS_DB, len*sizeof(uint32_t)); @@ -1532,6 +1575,28 @@ static int ls_format_attributes(struct stream *s, struct ls_attributes *attr) stream_put(s, attr->adj_sid[ADJ_BCK_IPV6].neighbor.sysid, ISO_SYS_ID_LEN); } + if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SRV6SID)) { + stream_put(s, &attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].sid, + sizeof(struct in6_addr)); + stream_putc(s, attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].flags); + stream_putc(s, attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].weight); + stream_putw(s, attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6] + .endpoint_behavior); + stream_put(s, + attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].neighbor.sysid, + ISO_SYS_ID_LEN); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SRV6SID)) { + stream_put(s, &attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].sid, + sizeof(struct in6_addr)); + stream_putc(s, attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].flags); + stream_putc(s, attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].weight); + stream_putw(s, attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6] + .endpoint_behavior); + stream_put(s, + attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].neighbor.sysid, + ISO_SYS_ID_LEN); + } if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) { stream_putc(s, attr->srlg_len); for (len = 0; len < attr->srlg_len; len++) @@ -2351,6 +2416,24 @@ static void ls_show_edge_vty(struct ls_edge *edge, struct vty *vty, attr->adj_sid[ADJ_BCK_IPV6].flags, attr->adj_sid[ADJ_BCK_IPV6].weight); } + if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SRV6SID)) { + sbuf_push(&sbuf, 4, "IPv6 Adjacency-SRV6-SID: %pI6", + &attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].sid); + sbuf_push(&sbuf, 0, + "\tFlags: 0x%x\tWeight: 0x%x\tbehavior: 0x%x\n", + attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].flags, + attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].weight, + attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].endpoint_behavior); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SRV6SID)) { + sbuf_push(&sbuf, 4, "IPv6 Bck. Adjacency-SRV6-SID: %pI6", + &attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].sid); + sbuf_push(&sbuf, 0, + "\tFlags: 0x%x\tWeight: 0x%x\tbehavior: 0x%x\n", + attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].flags, + attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].weight, + attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].endpoint_behavior); + } if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) { sbuf_push(&sbuf, 4, "SRLGs: %d", attr->srlg_len); for (int i = 1; i < attr->srlg_len; i++) { @@ -2372,7 +2455,7 @@ static void ls_show_edge_json(struct ls_edge *edge, struct json_object *json) struct ls_attributes *attr; struct json_object *jte, *jbw, *jobj, *jsr = NULL, *jsrlg, *js_ext_ag, *js_ext_ag_arr_word, - *js_ext_ag_arr_bit; + *js_ext_ag_arr_bit, *jsrv6 = NULL; char buf[INET6_BUFSIZ]; char buf_ag[strlen("0xffffffff") + 1]; uint32_t bitmap; @@ -2557,6 +2640,45 @@ static void ls_show_edge_json(struct ls_edge *edge, struct json_object *json) attr->adj_sid[ADJ_BCK_IPV6].weight); json_object_array_add(jsr, jobj); } + if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SRV6SID)) { + jsrv6 = json_object_new_array(); + json_object_object_add(json, "segment-routing-ipv6", jsrv6); + jobj = json_object_new_object(); + snprintfrr(buf, INET6_BUFSIZ, "%pI6", + &attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].sid); + json_object_string_add(jobj, "adj-sid", buf); + snprintfrr(buf, 6, "0x%x", + attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].flags); + json_object_string_add(jobj, "flags", buf); + json_object_int_add(jobj, "weight", + attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].weight); + snprintfrr(buf, 6, "0x%x", + attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6] + .endpoint_behavior); + json_object_string_add(jobj, "endpoint-behavior", buf); + json_object_array_add(jsr, jobj); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SRV6SID)) { + if (!jsrv6) { + jsrv6 = json_object_new_array(); + json_object_object_add(json, "segment-routing-ipv6", + jsrv6); + } + jobj = json_object_new_object(); + snprintfrr(buf, INET6_BUFSIZ, "%pI6", + &attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].sid); + json_object_string_add(jobj, "adj-sid", buf); + snprintfrr(buf, 6, "0x%x", + attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].flags); + json_object_string_add(jobj, "flags", buf); + json_object_int_add(jobj, "weight", + attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].weight); + snprintfrr(buf, 6, "0x%x", + attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6] + .endpoint_behavior); + json_object_string_add(jobj, "endpoint-behavior", buf); + json_object_array_add(jsr, jobj); + } } void ls_show_edge(struct ls_edge *edge, struct vty *vty, diff --git a/lib/link_state.h b/lib/link_state.h index d3a0ce3..d819c20 100644 --- a/lib/link_state.h +++ b/lib/link_state.h @@ -106,6 +106,7 @@ extern int ls_node_id_same(struct ls_node_id i1, struct ls_node_id i2); #define LS_NODE_SR 0x0040 #define LS_NODE_SRLB 0x0080 #define LS_NODE_MSD 0x0100 +#define LS_NODE_SRV6 0x0200 /* Link State Node structure */ struct ls_node { @@ -128,6 +129,14 @@ struct ls_node { } srlb; uint8_t algo[LIB_LS_SR_ALGO_COUNT]; /* Segment Routing Algorithms */ uint8_t msd; /* Maximum Stack Depth */ + + uint16_t srv6_cap_flags; /* draft-ietf-idr-bgpls-srv6-ext, 3.1., flags field */ + struct ls_srv6_msd { /* draft-ietf-idr-bgpls-srv6-ext, 3.2. */ + uint8_t max_seg_left_msd; + uint8_t max_end_pop_msd; + uint8_t max_h_encaps_msd; + uint8_t max_end_d_msd; + } srv6_msd; }; /* Link State flags to indicate which Attribute parameters are valid */ @@ -161,6 +170,8 @@ struct ls_node { #define LS_ATTR_BCK_ADJ_SID6 0x08000000 #define LS_ATTR_SRLG 0x10000000 #define LS_ATTR_EXT_ADM_GRP 0x20000000 +#define LS_ATTR_ADJ_SRV6SID 0x40000000 +#define LS_ATTR_BCK_ADJ_SRV6SID 0x80000000 /* Link State Attributes */ struct ls_attributes { @@ -209,6 +220,18 @@ struct ls_attributes { uint8_t sysid[ISO_SYS_ID_LEN]; /* or Sys-ID for ISIS */ } neighbor; } adj_sid[4]; /* IPv4/IPv6 & Primary/Backup (LAN)-Adj. SID */ +#define ADJ_SRV6_PRI_IPV6 0 +#define ADJ_SRV6_BCK_IPV6 1 +#define ADJ_SRV6_MAX 2 + struct ls_srv6_adjacency { /* Adjacency SID for IS-IS */ + struct in6_addr sid; /* SID as IPv6 address */ + uint8_t flags; /* Flags */ + uint8_t weight; /* Administrative weight */ + uint16_t endpoint_behavior; /* Endpoint Behavior */ + union { + uint8_t sysid[ISO_SYS_ID_LEN]; /* Sys-ID for ISIS */ + } neighbor; + } adj_srv6_sid[2]; uint32_t *srlgs; /* List of Shared Risk Link Group */ uint8_t srlg_len; /* number of SRLG in the list */ }; diff --git a/lib/memory.h b/lib/memory.h index ba437eb..65b99a5 100644 --- a/lib/memory.h +++ b/lib/memory.h @@ -69,14 +69,12 @@ struct memgroup { #define DECLARE_MGROUP(name) extern struct memgroup _mg_##name #define _DEFINE_MGROUP(mname, desc, ...) \ - struct memgroup _mg_##mname \ - __attribute__((section(".data.mgroups"))) = { \ - .name = desc, \ - .types = NULL, \ - .next = NULL, \ - .insert = NULL, \ - .ref = NULL, \ - __VA_ARGS__ \ + struct memgroup _mg_##mname _DATA_SECTION("mgroups") = { \ + .name = desc, \ + .types = NULL, \ + .next = NULL, \ + .insert = NULL, \ + .ref = NULL, \ }; \ static void _mginit_##mname(void) __attribute__((_CONSTRUCTOR(1000))); \ static void _mginit_##mname(void) \ @@ -105,13 +103,12 @@ struct memgroup { /* end */ #define DEFINE_MTYPE_ATTR(group, mname, attr, desc) \ - attr struct memtype MTYPE_##mname[1] \ - __attribute__((section(".data.mtypes"))) = { { \ - .name = desc, \ - .next = NULL, \ - .n_alloc = 0, \ - .size = 0, \ - .ref = NULL, \ + attr struct memtype MTYPE_##mname[1] _DATA_SECTION("mtypes") = { { \ + .name = desc, \ + .next = NULL, \ + .n_alloc = 0, \ + .size = 0, \ + .ref = NULL, \ } }; \ static void _mtinit_##mname(void) __attribute__((_CONSTRUCTOR(1001))); \ static void _mtinit_##mname(void) \ diff --git a/lib/mgmt.proto b/lib/mgmt.proto index 01a99ab..c953011 100644 --- a/lib/mgmt.proto +++ b/lib/mgmt.proto @@ -79,6 +79,7 @@ message BeSubscribeReq { repeated string config_xpaths = 2; repeated string oper_xpaths = 3; repeated string notif_xpaths = 4; + repeated string rpc_xpaths = 5; } message BeSubscribeReply { diff --git a/lib/mgmt_be_client.c b/lib/mgmt_be_client.c index f483d48..6e2fb05 100644 --- a/lib/mgmt_be_client.c +++ b/lib/mgmt_be_client.c @@ -915,6 +915,143 @@ static void be_client_handle_get_tree(struct mgmt_be_client *client, be_client_send_tree_data_batch, args); } +static void be_client_send_rpc_reply(struct mgmt_be_client *client, + uint64_t txn_id, uint64_t req_id, + uint8_t result_type, + struct lyd_node *output) +{ + struct mgmt_msg_rpc_reply *rpc_reply_msg; + uint8_t **darrp; + LY_ERR err; + int ret = NB_OK; + + rpc_reply_msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_rpc_reply, 0, + MTYPE_MSG_NATIVE_RPC_REPLY); + rpc_reply_msg->refer_id = txn_id; + rpc_reply_msg->req_id = req_id; + rpc_reply_msg->code = MGMT_MSG_CODE_RPC_REPLY; + rpc_reply_msg->result_type = result_type; + + if (output) { + darrp = mgmt_msg_native_get_darrp(rpc_reply_msg); + err = yang_print_tree_append(darrp, output, result_type, + LYD_PRINT_SHRINK); + lyd_free_all(output); + if (err) { + ret = NB_ERR; + goto done; + } + } + + (void)be_client_send_native_msg(client, rpc_reply_msg, + mgmt_msg_native_get_msg_len( + rpc_reply_msg), + false); +done: + mgmt_msg_native_free_msg(rpc_reply_msg); + if (ret != NB_OK) + be_client_send_error(client, txn_id, req_id, false, -EINVAL, + "Can't format RPC reply"); +} + +/* + * Process the RPC request. + */ +static void be_client_handle_rpc(struct mgmt_be_client *client, uint64_t txn_id, + void *msgbuf, size_t msg_len) +{ + struct mgmt_msg_rpc *rpc_msg = msgbuf; + struct nb_node *nb_node; + struct lyd_node *input, *output; + const char *xpath; + const char *data; + char errmsg[BUFSIZ] = { 0 }; + LY_ERR err; + int ret; + + debug_be_client("Received RPC request for client %s txn-id %" PRIu64 + " req-id %" PRIu64, + client->name, txn_id, rpc_msg->req_id); + + xpath = mgmt_msg_native_xpath_data_decode(rpc_msg, msg_len, data); + if (!xpath) { + be_client_send_error(client, txn_id, rpc_msg->req_id, false, + -EINVAL, "Corrupt RPC message"); + return; + } + + nb_node = nb_node_find(xpath); + if (!nb_node) { + be_client_send_error(client, txn_id, rpc_msg->req_id, false, + -EINVAL, "No schema found for RPC: %s", + xpath); + return; + } + + if (!nb_node->cbs.rpc) { + be_client_send_error(client, txn_id, rpc_msg->req_id, false, + -EINVAL, "No RPC callback for: %s", xpath); + return; + } + + if (data) { + err = yang_parse_rpc(xpath, rpc_msg->request_type, data, false, + &input); + if (err) { + be_client_send_error(client, txn_id, rpc_msg->req_id, + false, -EINVAL, + "Can't parse RPC data for: %s", + xpath); + return; + } + } else { + /* + * If there's no input data, create an empty input container. + * It is especially needed for actions, because their parents + * may hold necessary information. + */ + err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, 0, + NULL, &input); + if (err) { + be_client_send_error(client, txn_id, rpc_msg->req_id, + false, -EINVAL, + "Can't create input node for RPC: %s", + xpath); + return; + } + } + + err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, 0, NULL, + &output); + if (err) { + lyd_free_all(input); + be_client_send_error(client, txn_id, rpc_msg->req_id, false, + -EINVAL, + "Can't create output node for RPC: %s", + xpath); + return; + } + + ret = nb_callback_rpc(nb_node, xpath, input, output, errmsg, + sizeof(errmsg)); + if (ret != NB_OK) { + lyd_free_all(input); + lyd_free_all(output); + be_client_send_error(client, txn_id, rpc_msg->req_id, false, + -EINVAL, "%s", errmsg); + return; + } + + lyd_free_all(input); + if (!lyd_child(output)) { + lyd_free_all(output); + output = NULL; + } + + be_client_send_rpc_reply(client, txn_id, rpc_msg->req_id, + rpc_msg->request_type, output); +} + /* * Process the notification. */ @@ -975,6 +1112,9 @@ static void be_client_handle_native_msg(struct mgmt_be_client *client, case MGMT_MSG_CODE_GET_TREE: be_client_handle_get_tree(client, txn_id, msg, msg_len); break; + case MGMT_MSG_CODE_RPC: + be_client_handle_rpc(client, txn_id, msg, msg_len); + break; case MGMT_MSG_CODE_NOTIFY: be_client_handle_notify(client, msg, msg_len); break; @@ -1040,6 +1180,9 @@ int mgmt_be_send_subscr_req(struct mgmt_be_client *client_ctx, subscr_req.n_notif_xpaths = client_ctx->cbs.nnotif_xpaths; subscr_req.notif_xpaths = (char **)client_ctx->cbs.notif_xpaths; + subscr_req.n_rpc_xpaths = client_ctx->cbs.nrpc_xpaths; + subscr_req.rpc_xpaths = (char **)client_ctx->cbs.rpc_xpaths; + mgmtd__be_message__init(&be_msg); be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ; be_msg.subscr_req = &subscr_req; diff --git a/lib/mgmt_be_client.h b/lib/mgmt_be_client.h index cd8b237..7ad0589 100644 --- a/lib/mgmt_be_client.h +++ b/lib/mgmt_be_client.h @@ -75,6 +75,8 @@ struct mgmt_be_client_cbs { const char **notif_xpaths; uint nnotif_xpaths; + const char **rpc_xpaths; + uint nrpc_xpaths; }; /*************************************************************** diff --git a/lib/mgmt_fe_client.c b/lib/mgmt_fe_client.c index a107582..8cfb025 100644 --- a/lib/mgmt_fe_client.c +++ b/lib/mgmt_fe_client.c @@ -329,6 +329,63 @@ int mgmt_fe_send_get_data_req(struct mgmt_fe_client *client, return ret; } +int mgmt_fe_send_edit_req(struct mgmt_fe_client *client, uint64_t session_id, + uint64_t req_id, uint8_t datastore, + LYD_FORMAT request_type, uint8_t flags, + uint8_t operation, const char *xpath, const char *data) +{ + struct mgmt_msg_edit *msg; + int ret; + + msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_edit, 0, + MTYPE_MSG_NATIVE_EDIT); + msg->refer_id = session_id; + msg->req_id = req_id; + msg->code = MGMT_MSG_CODE_EDIT; + msg->request_type = request_type; + msg->flags = flags; + msg->datastore = datastore; + msg->operation = operation; + + mgmt_msg_native_xpath_encode(msg, xpath); + if (data) + mgmt_msg_native_append(msg, data, strlen(data) + 1); + + debug_fe_client("Sending EDIT_REQ session-id %" PRIu64 + " req-id %" PRIu64 " xpath: %s", + session_id, req_id, xpath); + + ret = mgmt_msg_native_send_msg(&client->client.conn, msg, false); + mgmt_msg_native_free_msg(msg); + return ret; +} + +int mgmt_fe_send_rpc_req(struct mgmt_fe_client *client, uint64_t session_id, + uint64_t req_id, LYD_FORMAT request_type, + const char *xpath, const char *data) +{ + struct mgmt_msg_rpc *msg; + int ret; + + msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_rpc, 0, + MTYPE_MSG_NATIVE_RPC); + msg->refer_id = session_id; + msg->req_id = req_id; + msg->code = MGMT_MSG_CODE_RPC; + msg->request_type = request_type; + + mgmt_msg_native_xpath_encode(msg, xpath); + if (data) + mgmt_msg_native_append(msg, data, strlen(data) + 1); + + debug_fe_client("Sending RPC_REQ session-id %" PRIu64 " req-id %" PRIu64 + " xpath: %s", + session_id, req_id, xpath); + + ret = mgmt_msg_native_send_msg(&client->client.conn, msg, false); + mgmt_msg_native_free_msg(msg); + return ret; +} static int mgmt_fe_client_handle_msg(struct mgmt_fe_client *client, Mgmtd__FeMessage *fe_msg) @@ -503,7 +560,10 @@ static void fe_client_handle_native_msg(struct mgmt_fe_client *client, struct mgmt_fe_client_session *session = NULL; struct mgmt_msg_notify_data *notify_msg; struct mgmt_msg_tree_data *tree_msg; + struct mgmt_msg_edit_reply *edit_msg; + struct mgmt_msg_rpc_reply *rpc_msg; struct mgmt_msg_error *err_msg; + const char *xpath = NULL; const char *data = NULL; size_t dlen; @@ -554,6 +614,45 @@ static void fe_client_handle_native_msg(struct mgmt_fe_client *client, msg_len - sizeof(*tree_msg), tree_msg->partial_error); break; + case MGMT_MSG_CODE_EDIT_REPLY: + if (!session->client->cbs.edit_notify) + return; + + edit_msg = (typeof(edit_msg))msg; + if (msg_len < sizeof(*edit_msg)) { + log_err_fe_client("Corrupt edit-reply msg recv"); + return; + } + + xpath = mgmt_msg_native_xpath_decode(edit_msg, msg_len); + if (!xpath) { + log_err_fe_client("Corrupt edit-reply msg recv"); + return; + } + + session->client->cbs.edit_notify(client, client->user_data, + session->client_id, + msg->refer_id, + session->user_ctx, msg->req_id, + xpath); + break; + case MGMT_MSG_CODE_RPC_REPLY: + if (!session->client->cbs.rpc_notify) + return; + + rpc_msg = (typeof(rpc_msg))msg; + if (msg_len < sizeof(*rpc_msg)) { + log_err_fe_client("Corrupt rpc-reply msg recv"); + return; + } + dlen = msg_len - sizeof(*rpc_msg); + + session->client->cbs.rpc_notify(client, client->user_data, + session->client_id, + msg->refer_id, + session->user_ctx, msg->req_id, + dlen ? rpc_msg->data : NULL); + break; case MGMT_MSG_CODE_NOTIFY: if (!session->client->cbs.async_notification) return; diff --git a/lib/mgmt_fe_client.h b/lib/mgmt_fe_client.h index eee4594..20c8704 100644 --- a/lib/mgmt_fe_client.h +++ b/lib/mgmt_fe_client.h @@ -114,6 +114,18 @@ struct mgmt_fe_client_cbs { LYD_FORMAT result_type, void *result, size_t len, int partial_error); + /* Called when edit result is returned */ + int (*edit_notify)(struct mgmt_fe_client *client, uintptr_t user_data, + uint64_t client_id, uint64_t session_id, + uintptr_t session_ctx, uint64_t req_id, + const char *xpath); + + /* Called when RPC result is returned */ + int (*rpc_notify)(struct mgmt_fe_client *client, uintptr_t user_data, + uint64_t client_id, uint64_t session_id, + uintptr_t session_ctx, uint64_t req_id, + const char *result); + /* Called with asynchronous notifications from backends */ int (*async_notification)(struct mgmt_fe_client *client, uintptr_t user_data, uint64_t client_id, @@ -410,6 +422,74 @@ extern int mgmt_fe_send_get_data_req(struct mgmt_fe_client *client, const char *xpath); /* + * Send EDIT to MGMTD daemon. + * + * client + * Client object. + * + * session_id + * Client session ID. + * + * req_id + * Client request ID. + * + * datastore + * Datastore for editing. + * + * request_type + * The LYD_FORMAT of the request. + * + * flags + * Flags to control the behavior of the request. + * + * operation + * NB_OP_* operation to perform. + * + * xpath + * the xpath to edit. + * + * data + * the data tree. + * + * Returns: + * 0 on success, otherwise msg_conn_send_msg() return values. + */ +extern int mgmt_fe_send_edit_req(struct mgmt_fe_client *client, + uint64_t session_id, uint64_t req_id, + uint8_t datastore, LYD_FORMAT request_type, + uint8_t flags, uint8_t operation, + const char *xpath, const char *data); + +/* + * Send RPC request to MGMTD daemon. + * + * client + * Client object. + * + * session_id + * Client session ID. + * + * req_id + * Client request ID. + * + * result_type + * The LYD_FORMAT of the result. + * + * xpath + * the xpath of the RPC. + * + * data + * the data tree. + * + * Returns: + * 0 on success, otherwise msg_conn_send_msg() return values. + */ +extern int mgmt_fe_send_rpc_req(struct mgmt_fe_client *client, + uint64_t session_id, uint64_t req_id, + LYD_FORMAT request_type, const char *xpath, + const char *data); + +/* * Destroy library and cleanup everything. */ extern void mgmt_fe_client_destroy(struct mgmt_fe_client *client); diff --git a/lib/mgmt_msg_native.c b/lib/mgmt_msg_native.c index d27c5d3..39ce9ab 100644 --- a/lib/mgmt_msg_native.c +++ b/lib/mgmt_msg_native.c @@ -9,12 +9,15 @@ #include "mgmt_msg_native.h" DEFINE_MGROUP(MSG_NATIVE, "Native message allocations"); -DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_MSG, "native mgmt msg"); DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_ERROR, "native error msg"); DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_GET_TREE, "native get tree msg"); DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_TREE_DATA, "native tree data msg"); DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_GET_DATA, "native get data msg"); DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_NOTIFY, "native get data msg"); +DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_EDIT, "native edit msg"); +DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_EDIT_REPLY, "native edit reply msg"); +DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_RPC, "native RPC msg"); +DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_RPC_REPLY, "native RPC reply msg"); int vmgmt_msg_native_send_error(struct msg_conn *conn, uint64_t sess_or_txn_id, uint64_t req_id, bool short_circuit_ok, diff --git a/lib/mgmt_msg_native.h b/lib/mgmt_msg_native.h index 53bb81b..21f702c 100644 --- a/lib/mgmt_msg_native.h +++ b/lib/mgmt_msg_native.h @@ -4,6 +4,15 @@ * * Copyright (c) 2023, LabN Consulting, L.L.C. * + * Public APIs: + * + * The message type codes and corresponding message data definitions for + * front-end client messages represent a public API, as such any changes should + * only be made according to backward compatible principles (basically never, + * just use a new message type). Back-end clients being always compiled with FRR + * can be updated (although one should take care in modifying BE messages as it + * could impact private back-end client implementations which will then need to + * be updated by their owners). */ #ifndef _FRR_MGMT_MSG_NATIVE_H_ @@ -21,6 +30,7 @@ extern "C" { #include "memory.h" #include "mgmt_msg.h" #include "mgmt_defines.h" +#include "northbound.h" #include <stdalign.h> @@ -149,15 +159,23 @@ DECLARE_MTYPE(MSG_NATIVE_GET_TREE); DECLARE_MTYPE(MSG_NATIVE_TREE_DATA); DECLARE_MTYPE(MSG_NATIVE_GET_DATA); DECLARE_MTYPE(MSG_NATIVE_NOTIFY); +DECLARE_MTYPE(MSG_NATIVE_EDIT); +DECLARE_MTYPE(MSG_NATIVE_EDIT_REPLY); +DECLARE_MTYPE(MSG_NATIVE_RPC); +DECLARE_MTYPE(MSG_NATIVE_RPC_REPLY); /* * Native message codes */ -#define MGMT_MSG_CODE_ERROR 0 -#define MGMT_MSG_CODE_GET_TREE 1 -#define MGMT_MSG_CODE_TREE_DATA 2 -#define MGMT_MSG_CODE_GET_DATA 3 -#define MGMT_MSG_CODE_NOTIFY 4 +#define MGMT_MSG_CODE_ERROR 0 /* Public API */ +#define MGMT_MSG_CODE_GET_TREE 1 /* BE only, non-public API */ +#define MGMT_MSG_CODE_TREE_DATA 2 /* Public API */ +#define MGMT_MSG_CODE_GET_DATA 3 /* Public API */ +#define MGMT_MSG_CODE_NOTIFY 4 /* Public API */ +#define MGMT_MSG_CODE_EDIT 5 /* Public API */ +#define MGMT_MSG_CODE_EDIT_REPLY 6 /* Public API */ +#define MGMT_MSG_CODE_RPC 7 /* Public API */ +#define MGMT_MSG_CODE_RPC_REPLY 8 /* Public API */ /* * Datastores @@ -318,6 +336,96 @@ _Static_assert(sizeof(struct mgmt_msg_notify_data) == offsetof(struct mgmt_msg_notify_data, data), "Size mismatch"); +#define EDIT_FLAG_IMPLICIT_LOCK 0x01 +#define EDIT_FLAG_IMPLICIT_COMMIT 0x02 + +#define EDIT_OP_CREATE 0 +#define EDIT_OP_DELETE 4 +#define EDIT_OP_MERGE 2 +#define EDIT_OP_REPLACE 5 +#define EDIT_OP_REMOVE 3 + +_Static_assert(EDIT_OP_CREATE == NB_OP_CREATE_EXCL, "Operation mismatch"); +_Static_assert(EDIT_OP_DELETE == NB_OP_DELETE, "Operation mismatch"); +_Static_assert(EDIT_OP_MERGE == NB_OP_MODIFY, "Operation mismatch"); +_Static_assert(EDIT_OP_REPLACE == NB_OP_REPLACE, "Operation mismatch"); +_Static_assert(EDIT_OP_REMOVE == NB_OP_DESTROY, "Operation mismatch"); + +/** + * struct mgmt_msg_edit - frontend edit request. + * + * @request_type: ``LYD_FORMAT`` for the @data. + * @flags: combination of ``EDIT_FLAG_*`` flags. + * @datastore: the datastore to edit. + * @operation: one of ``EDIT_OP_*`` operations. + * @data: the xpath followed by the tree data for the operation. + * for CREATE, xpath points to the parent node. + */ +struct mgmt_msg_edit { + struct mgmt_msg_header; + uint8_t request_type; + uint8_t flags; + uint8_t datastore; + uint8_t operation; + uint8_t resv2[4]; + + alignas(8) char data[]; +}; +_Static_assert(sizeof(struct mgmt_msg_edit) == + offsetof(struct mgmt_msg_edit, data), + "Size mismatch"); + +/** + * struct mgmt_msg_edit_reply - frontend edit reply. + * + * @data: the xpath of the data node that was created. + */ +struct mgmt_msg_edit_reply { + struct mgmt_msg_header; + uint8_t resv2[8]; + + alignas(8) char data[]; +}; +_Static_assert(sizeof(struct mgmt_msg_edit_reply) == + offsetof(struct mgmt_msg_edit_reply, data), + "Size mismatch"); + +/** + * struct mgmt_msg_rpc - RPC/action request. + * + * @request_type: ``LYD_FORMAT`` for the @data. + * @data: the xpath followed by the tree data for the operation. + */ +struct mgmt_msg_rpc { + struct mgmt_msg_header; + uint8_t request_type; + uint8_t resv2[7]; + + alignas(8) char data[]; +}; + +_Static_assert(sizeof(struct mgmt_msg_rpc) == + offsetof(struct mgmt_msg_rpc, data), + "Size mismatch"); + +/** + * struct mgmt_msg_rpc_reply - RPC/action reply. + * + * @result_type: ``LYD_FORMAT`` for the @data. + * @data: the tree data for the reply. + */ +struct mgmt_msg_rpc_reply { + struct mgmt_msg_header; + uint8_t result_type; + uint8_t resv2[7]; + + alignas(8) char data[]; +}; + +_Static_assert(sizeof(struct mgmt_msg_rpc_reply) == + offsetof(struct mgmt_msg_rpc_reply, data), + "Size mismatch"); + /* * Validate that the message ends in a NUL terminating byte */ @@ -504,13 +612,16 @@ extern int vmgmt_msg_native_send_error(struct msg_conn *conn, * The xpath string or NULL if there was an error decoding (i.e., the * message is corrupt). */ -#define mgmt_msg_native_xpath_data_decode(msg, msglen, data) \ +#define mgmt_msg_native_xpath_data_decode(msg, msglen, __data) \ ({ \ size_t __len = (msglen) - sizeof(*msg); \ const char *__s = NULL; \ if (msg->vsplit && msg->vsplit <= __len && \ msg->data[msg->vsplit - 1] == 0) { \ - (data) = msg->data + msg->vsplit; \ + if (msg->vsplit < __len) \ + (__data) = msg->data + msg->vsplit; \ + else \ + (__data) = NULL; \ __s = msg->data; \ } \ __s; \ diff --git a/lib/netns_other.c b/lib/netns_other.c index 3021840..545a962 100644 --- a/lib/netns_other.c +++ b/lib/netns_other.c @@ -13,10 +13,6 @@ #include "log.h" #include "memory.h" -DEFINE_MTYPE_STATIC(LIB, NS, "NetNS Context"); -DEFINE_MTYPE_STATIC(LIB, NS_NAME, "NetNS Name"); - - static inline int ns_compare(const struct ns *ns, const struct ns *ns2); RB_GENERATE(ns_head, ns, entry, ns_compare) @@ -39,7 +35,6 @@ void ns_init_management(ns_id_t ns_id) { } - /* * NS utilities */ diff --git a/lib/nexthop.c b/lib/nexthop.c index 243b52d..26c3382 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -1154,3 +1154,346 @@ bool nexthop_is_blackhole(const struct nexthop *nh) { return nh->type == NEXTHOP_TYPE_BLACKHOLE; } + +/* + * Render a nexthop into a json object; the caller allocates and owns + * the json object memory. + */ +void nexthop_json_helper(json_object *json_nexthop, + const struct nexthop *nexthop, bool display_vrfid, + uint8_t rn_family) +{ + json_object *json_labels = NULL; + json_object *json_backups = NULL; + json_object *json_seg6local = NULL; + json_object *json_seg6 = NULL; + json_object *json_segs = NULL; + int i; + + json_object_int_add(json_nexthop, "flags", nexthop->flags); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) + json_object_boolean_true_add(json_nexthop, "duplicate"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + json_object_boolean_true_add(json_nexthop, "fib"); + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + json_object_string_addf(json_nexthop, "ip", "%pI4", + &nexthop->gate.ipv4); + json_object_string_add(json_nexthop, "afi", "ipv4"); + + if (nexthop->ifindex) { + json_object_int_add(json_nexthop, "interfaceIndex", + nexthop->ifindex); + json_object_string_add(json_nexthop, "interfaceName", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + } + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + json_object_string_addf(json_nexthop, "ip", "%pI6", + &nexthop->gate.ipv6); + json_object_string_add(json_nexthop, "afi", "ipv6"); + + if (nexthop->ifindex) { + json_object_int_add(json_nexthop, "interfaceIndex", + nexthop->ifindex); + json_object_string_add(json_nexthop, "interfaceName", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + } + break; + + case NEXTHOP_TYPE_IFINDEX: + json_object_boolean_true_add(json_nexthop, "directlyConnected"); + json_object_int_add(json_nexthop, "interfaceIndex", + nexthop->ifindex); + json_object_string_add(json_nexthop, "interfaceName", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + json_object_boolean_true_add(json_nexthop, "unreachable"); + switch (nexthop->bh_type) { + case BLACKHOLE_REJECT: + json_object_boolean_true_add(json_nexthop, "reject"); + break; + case BLACKHOLE_ADMINPROHIB: + json_object_boolean_true_add(json_nexthop, + "adminProhibited"); + break; + case BLACKHOLE_NULL: + json_object_boolean_true_add(json_nexthop, "blackhole"); + break; + case BLACKHOLE_UNSPEC: + break; + } + break; + } + + /* This nexthop is a resolver for the parent nexthop. + * Set resolver flag for better clarity and delimiter + * in flat list of nexthops in json. + */ + if (nexthop->rparent) + json_object_boolean_true_add(json_nexthop, "resolver"); + + if (display_vrfid) + json_object_string_add(json_nexthop, "vrf", + vrf_id_to_name(nexthop->vrf_id)); + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) + json_object_boolean_true_add(json_nexthop, "duplicate"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + json_object_boolean_true_add(json_nexthop, "active"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) + json_object_boolean_true_add(json_nexthop, "onLink"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_LINKDOWN)) + json_object_boolean_true_add(json_nexthop, "linkDown"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + json_object_boolean_true_add(json_nexthop, "recursive"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + json_backups = json_object_new_array(); + for (i = 0; i < nexthop->backup_num; i++) { + json_object_array_add(json_backups, + json_object_new_int( + nexthop->backup_idx[i])); + } + + json_object_object_add(json_nexthop, "backupIndex", + json_backups); + } + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + if (nexthop->rmap_src.ipv4.s_addr) + json_object_string_addf(json_nexthop, "rmapSource", + "%pI4", &nexthop->rmap_src.ipv4); + else if (nexthop->src.ipv4.s_addr) + json_object_string_addf(json_nexthop, "source", "%pI4", + &nexthop->src.ipv4); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + /* Allow for 5549 ipv4 prefix with ipv6 nexthop */ + if (rn_family == AF_INET && nexthop->rmap_src.ipv4.s_addr) + json_object_string_addf(json_nexthop, "rmapSource", + "%pI4", &nexthop->rmap_src.ipv4); + else if (!IPV6_ADDR_SAME(&nexthop->rmap_src.ipv6, &in6addr_any)) + json_object_string_addf(json_nexthop, "rmapSource", + "%pI6", &nexthop->rmap_src.ipv6); + else if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any)) + json_object_string_addf(json_nexthop, "source", "%pI6", + &nexthop->src.ipv6); + break; + case NEXTHOP_TYPE_IFINDEX: + case NEXTHOP_TYPE_BLACKHOLE: + break; + } + + if (nexthop->nh_label && nexthop->nh_label->num_labels) { + json_labels = json_object_new_array(); + + for (int label_index = 0; + label_index < nexthop->nh_label->num_labels; label_index++) + json_object_array_add( + json_labels, + json_object_new_int(( + (nexthop->nh_label_type == ZEBRA_LSP_EVPN) + ? label2vni( + &nexthop->nh_label->label + [label_index]) + : nexthop->nh_label + ->label[label_index]))); + + json_object_object_add(json_nexthop, "labels", json_labels); + } + + if (nexthop->weight) + json_object_int_add(json_nexthop, "weight", nexthop->weight); + + if (nexthop->srte_color) + json_object_int_add(json_nexthop, "srteColor", + nexthop->srte_color); + + if (nexthop->nh_srv6) { + json_seg6local = json_object_new_object(); + json_object_string_add(json_seg6local, "action", + seg6local_action2str( + nexthop->nh_srv6 + ->seg6local_action)); + json_object_object_add(json_nexthop, "seg6local", + json_seg6local); + if (nexthop->nh_srv6->seg6_segs && + nexthop->nh_srv6->seg6_segs->num_segs == 1) { + json_seg6 = json_object_new_object(); + json_object_string_addf(json_seg6, "segs", "%pI6", + &nexthop->nh_srv6->seg6_segs + ->seg[0]); + json_object_object_add(json_nexthop, "seg6", json_seg6); + } else { + if (nexthop->nh_srv6->seg6_segs) { + json_segs = json_object_new_array(); + for (int seg_idx = 0; + seg_idx < + nexthop->nh_srv6->seg6_segs->num_segs; + seg_idx++) + json_object_array_add( + json_segs, + json_object_new_stringf( + "%pI6", + &nexthop->nh_srv6 + ->seg6_segs + ->seg[seg_idx])); + json_object_object_add(json_nexthop, "seg6", + json_segs); + } + } + } +} + +/* + * Helper for nexthop output + */ +void nexthop_vty_helper(struct vty *vty, const struct nexthop *nexthop, + bool display_vrfid, uint8_t rn_family) +{ + char buf[MPLS_LABEL_STRLEN]; + char seg_buf[SRV6_SEG_STRLEN]; + struct seg6_segs segs; + uint8_t i; + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + vty_out(vty, " via %pI4", &nexthop->gate.ipv4); + if (nexthop->ifindex) + vty_out(vty, ", %s", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + vty_out(vty, " via %s", + inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, + sizeof(buf))); + if (nexthop->ifindex) + vty_out(vty, ", %s", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + + case NEXTHOP_TYPE_IFINDEX: + vty_out(vty, " is directly connected, %s", + ifindex2ifname(nexthop->ifindex, nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + vty_out(vty, " unreachable"); + switch (nexthop->bh_type) { + case BLACKHOLE_REJECT: + vty_out(vty, " (ICMP unreachable)"); + break; + case BLACKHOLE_ADMINPROHIB: + vty_out(vty, " (ICMP admin-prohibited)"); + break; + case BLACKHOLE_NULL: + vty_out(vty, " (blackhole)"); + break; + case BLACKHOLE_UNSPEC: + break; + } + break; + } + + if (display_vrfid) + vty_out(vty, " (vrf %s)", vrf_id_to_name(nexthop->vrf_id)); + + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + vty_out(vty, " inactive"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) + vty_out(vty, " onlink"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_LINKDOWN)) + vty_out(vty, " linkdown"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + vty_out(vty, " (recursive)"); + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + if (nexthop->rmap_src.ipv4.s_addr) + vty_out(vty, ", rmapsrc %pI4", &nexthop->rmap_src.ipv4); + else if (nexthop->src.ipv4.s_addr) + vty_out(vty, ", src %pI4", &nexthop->src.ipv4); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + /* Allow for 5549 ipv4 prefix with ipv6 nexthop */ + if (rn_family == AF_INET && nexthop->rmap_src.ipv4.s_addr) + vty_out(vty, ", rmapsrc %pI4", &nexthop->rmap_src.ipv4); + else if (!IPV6_ADDR_SAME(&nexthop->rmap_src.ipv6, &in6addr_any)) + vty_out(vty, ", rmapsrc %pI6", &nexthop->rmap_src.ipv6); + else if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any)) + vty_out(vty, ", src %pI6", &nexthop->src.ipv6); + break; + case NEXTHOP_TYPE_IFINDEX: + case NEXTHOP_TYPE_BLACKHOLE: + break; + } + + /* SR-TE information */ + if (nexthop->srte_color) + vty_out(vty, ", SR-TE color %u", nexthop->srte_color); + + /* Label information */ + if (nexthop->nh_label && nexthop->nh_label->num_labels) { + vty_out(vty, ", label %s", + mpls_label2str(nexthop->nh_label->num_labels, + nexthop->nh_label->label, buf, + sizeof(buf), nexthop->nh_label_type, 1)); + } + + if (nexthop->nh_srv6) { + seg6local_context2str(buf, sizeof(buf), + &nexthop->nh_srv6->seg6local_ctx, + nexthop->nh_srv6->seg6local_action); + if (nexthop->nh_srv6->seg6local_action != + ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) + vty_out(vty, ", seg6local %s %s", + seg6local_action2str( + nexthop->nh_srv6->seg6local_action), + buf); + if (nexthop->nh_srv6->seg6_segs && + IPV6_ADDR_CMP(&nexthop->nh_srv6->seg6_segs->seg[0], + &in6addr_any)) { + segs.num_segs = nexthop->nh_srv6->seg6_segs->num_segs; + for (i = 0; i < segs.num_segs; i++) + memcpy(&segs.segs[i], + &nexthop->nh_srv6->seg6_segs->seg[i], + sizeof(struct in6_addr)); + snprintf_seg6_segs(seg_buf, SRV6_SEG_STRLEN, &segs); + vty_out(vty, ", seg6 %s", seg_buf); + } + } + + if (nexthop->weight) + vty_out(vty, ", weight %u", nexthop->weight); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + vty_out(vty, ", backup %d", nexthop->backup_idx[0]); + + for (i = 1; i < nexthop->backup_num; i++) + vty_out(vty, ",%d", nexthop->backup_idx[i]); + } +} diff --git a/lib/nexthop.h b/lib/nexthop.h index 958d06a..27073b9 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -252,6 +252,12 @@ extern bool nexthop_is_blackhole(const struct nexthop *nh); int nexthop_str2backups(const char *str, int *num_backups, uint8_t *backups); +void nexthop_json_helper(json_object *json_nexthop, + const struct nexthop *nexthop, bool display_vrfid, + uint8_t rn_family); +void nexthop_vty_helper(struct vty *vty, const struct nexthop *nexthop, + bool display_vrfid, uint8_t rn_family); + #ifdef _FRR_ATTRIBUTE_PRINTFRR #pragma FRR printfrr_ext "%pNH" (struct nexthop *) #endif diff --git a/lib/northbound.c b/lib/northbound.c index 487f225..0bc79d0 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -813,6 +813,231 @@ int nb_candidate_edit(struct nb_config *candidate, const struct nb_node *nb_node return NB_OK; } +static int nb_candidate_edit_tree_add(struct nb_config *candidate, + enum nb_operation operation, + LYD_FORMAT format, const char *xpath, + const char *data, char *xpath_created, + char *errmsg, size_t errmsg_len) +{ + struct lyd_node *tree = NULL; + struct lyd_node *parent = NULL; + struct lyd_node *dnode = NULL; + struct lyd_node *existing = NULL; + struct lyd_node *ex_parent = NULL; + char *parent_xpath = NULL; + struct ly_in *in; + LY_ERR err; + bool root; + int ret; + + ly_in_new_memory(data, &in); + + root = xpath[0] == 0 || (xpath[0] == '/' && xpath[1] == 0); + + /* get parent xpath if xpath is not root */ + if (!root) { + /* NB_OP_CREATE_EXCT already expects parent xpath */ + parent_xpath = XSTRDUP(MTYPE_TMP, xpath); + + /* for other operations - pop one level */ + if (operation != NB_OP_CREATE_EXCL) { + ret = yang_xpath_pop_node(parent_xpath); + if (ret) { + snprintf(errmsg, errmsg_len, "Invalid xpath"); + goto done; + } + + /* root is not actually a parent */ + if (parent_xpath[0] == 0) + XFREE(MTYPE_TMP, parent_xpath); + } + } + + /* + * Create parent if it's not root. We're creating a new tree here to be + * merged later with candidate. + */ + if (parent_xpath) { + err = lyd_new_path2(NULL, ly_native_ctx, parent_xpath, NULL, 0, + 0, 0, &tree, &parent); + if (err) { + yang_print_errors(ly_native_ctx, errmsg, errmsg_len); + ret = NB_ERR; + goto done; + } + assert(parent); + } + + /* parse data */ + err = yang_lyd_parse_data(ly_native_ctx, parent, in, format, + LYD_PARSE_ONLY | LYD_PARSE_STRICT | + LYD_PARSE_NO_STATE, + 0, &dnode); + if (err) { + yang_print_errors(ly_native_ctx, errmsg, errmsg_len); + ret = NB_ERR; + goto done; + } + + /* set the tree if we created a top-level node */ + if (!parent) + tree = dnode; + + /* save xpath of the created node */ + lyd_path(dnode, LYD_PATH_STD, xpath_created, XPATH_MAXLEN); + + /* verify that list keys are the same in the xpath and the data tree */ + if (!root && (operation == NB_OP_REPLACE || operation == NB_OP_MODIFY)) { + if (lyd_find_path(tree, xpath, 0, NULL)) { + snprintf(errmsg, errmsg_len, + "List keys in xpath and data tree are different"); + ret = NB_ERR; + goto done; + } + } + + /* check if the node already exists in candidate */ + if (operation == NB_OP_CREATE_EXCL || operation == NB_OP_REPLACE) { + existing = yang_dnode_get(candidate->dnode, xpath_created); + + /* if the existing node is implicit default, ignore */ + if (existing && (existing->flags & LYD_DEFAULT)) + existing = NULL; + + if (existing) { + if (operation == NB_OP_CREATE_EXCL) { + snprintf(errmsg, errmsg_len, + "Data already exists"); + ret = NB_ERR; + goto done; + } + + if (root) { + candidate->dnode = NULL; + } else { + /* if it's the first top-level node, update candidate */ + if (candidate->dnode == existing) + candidate->dnode = + candidate->dnode->next; + + ex_parent = lyd_parent(existing); + lyd_unlink_tree(existing); + } + } + } + + err = lyd_merge_siblings(&candidate->dnode, tree, + LYD_MERGE_DESTRUCT | LYD_MERGE_WITH_FLAGS); + if (err) { + /* if replace failed, restore the original node */ + if (existing) { + if (root) { + /* Restoring the whole config. */ + candidate->dnode = existing; + } else if (ex_parent) { + /* + * Restoring a nested node. Insert it as a + * child. + */ + lyd_insert_child(ex_parent, existing); + } else { + /* + * Restoring a top-level node. Insert it as a + * sibling to candidate->dnode to make sure + * the linkage is correct. + */ + lyd_insert_sibling(candidate->dnode, existing, + &candidate->dnode); + } + } + yang_print_errors(ly_native_ctx, errmsg, errmsg_len); + ret = NB_ERR; + goto done; + } else { + /* + * Free existing node after replace. + * We're using `lyd_free_siblings` here to free the whole + * tree if we replaced the root node. It won't affect other + * siblings if it wasn't root, because the existing node + * was unlinked from the tree. + */ + if (existing) + lyd_free_siblings(existing); + + tree = NULL; /* LYD_MERGE_DESTRUCT deleted the tree */ + } + + ret = NB_OK; +done: + if (tree) + lyd_free_all(tree); + XFREE(MTYPE_TMP, parent_xpath); + ly_in_free(in, 0); + + return ret; +} + +static int nb_candidate_edit_tree_del(struct nb_config *candidate, + enum nb_operation operation, + const char *xpath, char *errmsg, + size_t errmsg_len) +{ + struct lyd_node *dnode; + + /* deleting root - remove the whole config */ + if (xpath[0] == 0 || (xpath[0] == '/' && xpath[1] == 0)) { + lyd_free_all(candidate->dnode); + candidate->dnode = NULL; + return NB_OK; + } + + dnode = yang_dnode_get(candidate->dnode, xpath); + if (!dnode || (dnode->flags & LYD_DEFAULT)) { + if (operation == NB_OP_DELETE) { + snprintf(errmsg, errmsg_len, "Data missing"); + return NB_ERR; + } else + return NB_OK; + } + + /* if it's the first top-level node, update candidate */ + if (candidate->dnode == dnode) + candidate->dnode = candidate->dnode->next; + + lyd_free_tree(dnode); + + return NB_OK; +} + +int nb_candidate_edit_tree(struct nb_config *candidate, + enum nb_operation operation, LYD_FORMAT format, + const char *xpath, const char *data, + char *xpath_created, char *errmsg, size_t errmsg_len) +{ + int ret = NB_ERR; + + switch (operation) { + case NB_OP_CREATE_EXCL: + case NB_OP_CREATE: + case NB_OP_MODIFY: + case NB_OP_REPLACE: + ret = nb_candidate_edit_tree_add(candidate, operation, format, + xpath, data, xpath_created, + errmsg, errmsg_len); + break; + case NB_OP_DESTROY: + case NB_OP_DELETE: + ret = nb_candidate_edit_tree_del(candidate, operation, xpath, + errmsg, errmsg_len); + break; + case NB_OP_MOVE: + /* not supported yet */ + break; + } + + return ret; +} + const char *nb_operation_name(enum nb_operation operation) { switch (operation) { @@ -1603,14 +1828,11 @@ const void *nb_callback_lookup_next(const struct nb_node *nb_node, } int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath, - const struct list *input, struct list *output, char *errmsg, - size_t errmsg_len) + const struct lyd_node *input, struct lyd_node *output, + char *errmsg, size_t errmsg_len) { struct nb_cb_rpc_args args = {}; - if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS)) - return 0; - DEBUGD(&nb_dbg_cbs_rpc, "northbound RPC: %s", xpath); args.xpath = xpath; @@ -2403,8 +2625,6 @@ const char *nb_client_name(enum nb_client client) switch (client) { case NB_CLIENT_CLI: return "CLI"; - case NB_CLIENT_CONFD: - return "ConfD"; case NB_CLIENT_SYSREPO: return "Sysrepo"; case NB_CLIENT_GRPC: diff --git a/lib/northbound.h b/lib/northbound.h index 5be111c..34d17a5 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -274,11 +274,11 @@ struct nb_cb_rpc_args { /* XPath of the YANG RPC or action. */ const char *xpath; - /* Read-only list of input parameters. */ - const struct list *input; + /* Read-only "input" tree of the RPC/action. */ + const struct lyd_node *input; - /* List of output parameters to be populated by the callback. */ - struct list *output; + /* The "output" tree of the RPC/action to be populated by the callback. */ + struct lyd_node *output; /* Buffer to store human-readable error message in case of error. */ char *errmsg; @@ -621,11 +621,6 @@ struct nb_node { /* Flags. */ uint8_t flags; - -#ifdef HAVE_CONFD - /* ConfD hash value corresponding to this YANG path. */ - int confd_hash; -#endif }; /* The YANG container or list contains only config data. */ #define F_NB_NODE_CONFIG_ONLY 0x01 @@ -700,7 +695,6 @@ enum nb_error { enum nb_client { NB_CLIENT_NONE = 0, NB_CLIENT_CLI, - NB_CLIENT_CONFD, NB_CLIENT_SYSREPO, NB_CLIENT_GRPC, NB_CLIENT_PCEP, @@ -839,7 +833,7 @@ extern const void *nb_callback_lookup_next(const struct nb_node *nb_node, const void *parent_list_entry, const struct yang_list_keys *keys); extern int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath, - const struct list *input, struct list *output, + 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, struct lyd_node *dnode); @@ -1005,6 +999,44 @@ extern int nb_candidate_edit(struct nb_config *candidate, const struct yang_data *data); /* + * Edit a candidate configuration. Value is given as JSON/XML. + * + * candidate + * Candidate configuration to edit. + * + * operation + * Operation to apply. + * + * format + * LYD_FORMAT of the value. + * + * xpath + * XPath of the configuration node being edited. + * For create, it must be the parent. + * + * data + * New data tree for the node. + * + * xpath_created + * XPath of the created node if operation is "create". + * + * errmsg + * Buffer to store human-readable error message in case of error. + * + * errmsg_len + * Size of errmsg. + * + * Returns: + * - NB_OK on success. + * - NB_ERR for other errors. + */ +extern int nb_candidate_edit_tree(struct nb_config *candidate, + enum nb_operation operation, + LYD_FORMAT format, const char *xpath, + const char *data, char *xpath_created, + char *errmsg, size_t errmsg_len); + +/* * Create diff for configuration. * * dnode diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index 8809ec2..4f962cd 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -275,10 +275,31 @@ int nb_cli_apply_changes_clear_pending(struct vty *vty, return nb_cli_apply_changes_internal(vty, xpath_base_abs, true); } -int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input, - struct list *output) +int nb_cli_rpc_enqueue(struct vty *vty, const char *xpath, const char *value) +{ + struct nb_cfg_change *param; + + if (vty->num_rpc_params == VTY_MAXCFGCHANGES) { + /* Not expected to happen. */ + vty_out(vty, + "%% Exceeded the maximum number of params (%u) for a single command\n\n", + VTY_MAXCFGCHANGES); + return CMD_WARNING; + } + + param = &vty->rpc_params[vty->num_rpc_params++]; + strlcpy(param->xpath, xpath, sizeof(param->xpath)); + param->value = value; + + return CMD_SUCCESS; +} + +int nb_cli_rpc(struct vty *vty, const char *xpath, struct lyd_node **output_p) { struct nb_node *nb_node; + struct lyd_node *input = NULL; + struct lyd_node *output = NULL; + LY_ERR err; int ret; char errmsg[BUFSIZ] = {0}; @@ -289,12 +310,62 @@ int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input, return CMD_WARNING; } + /* create input tree */ + err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, 0, NULL, + &input); + assert(err == LY_SUCCESS); + + for (size_t i = 0; i < vty->num_rpc_params; i++) { + err = lyd_new_path(input, ly_native_ctx, + vty->rpc_params[i].xpath, + vty->rpc_params[i].value, 0, NULL); + assert(err == LY_SUCCESS); + } + + if (vty_mgmt_fe_enabled()) { + char *data = NULL; + + err = lyd_print_mem(&data, input, LYD_JSON, LYD_PRINT_SHRINK); + assert(err == LY_SUCCESS); + + ret = vty_mgmt_send_rpc_req(vty, LYD_JSON, xpath, data); + + free(data); + lyd_free_all(input); + + if (ret < 0) + return CMD_WARNING; + return CMD_SUCCESS; + } + + /* validate input tree to create implicit defaults */ + err = lyd_validate_op(input, NULL, LYD_TYPE_RPC_YANG, NULL); + assert(err == LY_SUCCESS); + + /* create output tree root for population in the callback */ + err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, 0, NULL, + &output); + assert(err == LY_SUCCESS); + ret = nb_callback_rpc(nb_node, xpath, input, output, errmsg, sizeof(errmsg)); + + /* validate output tree to create implicit defaults */ + err = lyd_validate_op(output, NULL, LYD_TYPE_REPLY_YANG, NULL); + assert(err == LY_SUCCESS); + + lyd_free_all(input); + vty->num_rpc_params = 0; + switch (ret) { case NB_OK: + if (output_p) + *output_p = output; + else + lyd_free_all(output); return CMD_SUCCESS; default: + lyd_free_all(output); if (strlen(errmsg)) vty_show_nb_errors(vty, ret, errmsg); return CMD_WARNING; diff --git a/lib/northbound_cli.h b/lib/northbound_cli.h index 1a45794..4c8dc50 100644 --- a/lib/northbound_cli.h +++ b/lib/northbound_cli.h @@ -80,7 +80,23 @@ extern int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...) PRINTFRR(2, 3); /* - * Execute a YANG RPC or Action. + * Add an input child node for an RPC or an action. + * + * vty + * The vty context. + * + * xpath + * XPath of the child being added, relative to the input container. + * + * value + * Value of the child being added. Can be NULL for containers and leafs of + * type 'empty'. + */ +extern int nb_cli_rpc_enqueue(struct vty *vty, const char *xpath, + const char *value); + +/* + * Execute a YANG RPC or Action using the enqueued input parameters. * * vty * The vty terminal to dump any error. @@ -88,20 +104,16 @@ extern int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, * xpath * XPath of the YANG RPC or Action node. * - * input - * List of 'yang_data' structures containing the RPC input parameters. It - * can be set to NULL when there are no input parameters. - * - * output - * List of 'yang_data' structures used to retrieve the RPC output parameters. - * It can be set to NULL when it's known that the given YANG RPC or Action - * doesn't have any output parameters. + * output_p + * A pointer to the libyang data node that will hold the output data tree. + * It can be set to NULL if the caller is not interested in processing the + * output. The caller is responsible for freeing the output data tree. * * Returns: * CMD_SUCCESS on success, CMD_WARNING otherwise. */ -extern int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input, - struct list *output); +extern int nb_cli_rpc(struct vty *vty, const char *xpath, + struct lyd_node **output_p); /* * Show CLI commands associated to the given YANG data node. diff --git a/lib/northbound_confd.c b/lib/northbound_confd.c deleted file mode 100644 index 8503d18..0000000 --- a/lib/northbound_confd.c +++ /dev/null @@ -1,1494 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2018 NetDEF, Inc. - * Renato Westphal - */ - -#include <zebra.h> - -#include "log.h" -#include "lib_errors.h" -#include "command.h" -#include "debug.h" -#include "libfrr.h" -#include "lib/version.h" -#include "northbound.h" - -#include <confd_lib.h> -#include <confd_cdb.h> -#include <confd_dp.h> -#include <confd_maapi.h> - -DEFINE_MTYPE_STATIC(LIB, CONFD, "ConfD module"); - -static struct debug nb_dbg_client_confd = {0, "Northbound client: ConfD"}; - -static struct event_loop *master; -static struct sockaddr confd_addr; -static int cdb_sub_sock, dp_ctl_sock, dp_worker_sock; -static struct event *t_cdb_sub, *t_dp_ctl, *t_dp_worker; -static struct confd_daemon_ctx *dctx; -static struct confd_notification_ctx *live_ctx; -static bool confd_connected; -static struct list *confd_spoints; -static struct nb_transaction *transaction; - -static void frr_confd_finish_cdb(void); -static void frr_confd_finish_dp(void); -static int frr_confd_finish(void); - -#define flog_err_confd(funcname) \ - flog_err(EC_LIB_LIBCONFD, "%s: %s() failed: %s (%d): %s", __func__, \ - (funcname), confd_strerror(confd_errno), confd_errno, \ - confd_lasterr()) - - -/* ------------ Utils ------------ */ - -/* Get XPath string from ConfD hashed keypath. */ -static void frr_confd_get_xpath(const confd_hkeypath_t *kp, char *xpath, - size_t len) -{ - char *p; - - confd_xpath_pp_kpath(xpath, len, 0, kp); - - /* - * Replace double quotes by single quotes (the format accepted by the - * northbound API). - */ - p = xpath; - while ((p = strchr(p, '"')) != NULL) - *p++ = '\''; -} - -/* Convert ConfD binary value to a string. */ -static int frr_confd_val2str(const char *xpath, const confd_value_t *value, - char *string, size_t string_size) -{ - struct confd_cs_node *csp; - - csp = confd_cs_node_cd(NULL, xpath); - if (!csp) { - flog_err_confd("confd_cs_node_cd"); - return -1; - } - if (confd_val2str(csp->info.type, value, string, string_size) - == CONFD_ERR) { - flog_err_confd("confd_val2str"); - return -1; - } - - return 0; -} - -/* Obtain list entry from ConfD hashed keypath. */ -static int frr_confd_hkeypath_get_list_entry(const confd_hkeypath_t *kp, - struct nb_node *nb_node, - const void **list_entry) -{ - struct nb_node *nb_node_list; - int parent_lists = 0; - int curr_list = 0; - - *list_entry = NULL; - - /* - * Count the number of YANG lists in the path, disconsidering the - * last element. - */ - nb_node_list = nb_node; - while (nb_node_list->parent_list) { - nb_node_list = nb_node_list->parent_list; - parent_lists++; - } - if (nb_node->snode->nodetype != LYS_LIST && parent_lists == 0) - return 0; - - /* Start from the beginning and move down the tree. */ - for (int i = kp->len; i >= 0; i--) { - struct yang_list_keys keys; - - /* Not a YANG list. */ - if (kp->v[i][0].type != C_BUF) - continue; - - /* Obtain list keys. */ - memset(&keys, 0, sizeof(keys)); - for (int j = 0; kp->v[i][j].type != C_NOEXISTS; j++) { - strlcpy(keys.key[keys.num], - (char *)kp->v[i][j].val.buf.ptr, - sizeof(keys.key[keys.num])); - keys.num++; - } - - /* Obtain northbound node associated to the YANG list. */ - nb_node_list = nb_node; - for (int j = curr_list; j < parent_lists; j++) - nb_node_list = nb_node_list->parent_list; - - /* Obtain list entry. */ - if (!CHECK_FLAG(nb_node_list->flags, F_NB_NODE_KEYLESS_LIST)) { - *list_entry = nb_callback_lookup_entry( - nb_node, *list_entry, &keys); - if (*list_entry == NULL) - return -1; - } else { - unsigned long ptr_ulong; - - /* Retrieve list entry from pseudo-key (string). */ - if (sscanf(keys.key[0], "%lu", &ptr_ulong) != 1) - return -1; - *list_entry = (const void *)ptr_ulong; - } - - curr_list++; - } - - return 0; -} - -/* Fill the current date and time into a confd_datetime structure. */ -static void getdatetime(struct confd_datetime *datetime) -{ - struct tm tm; - struct timeval tv; - - gettimeofday(&tv, NULL); - gmtime_r(&tv.tv_sec, &tm); - - memset(datetime, 0, sizeof(*datetime)); - datetime->year = 1900 + tm.tm_year; - datetime->month = tm.tm_mon + 1; - datetime->day = tm.tm_mday; - datetime->sec = tm.tm_sec; - datetime->micro = tv.tv_usec; - datetime->timezone = 0; - datetime->timezone_minutes = 0; - datetime->hour = tm.tm_hour; - datetime->min = tm.tm_min; -} - -/* ------------ CDB code ------------ */ - -struct cdb_iter_args { - struct nb_config *candidate; - bool error; -}; - -static enum cdb_iter_ret -frr_confd_cdb_diff_iter(confd_hkeypath_t *kp, enum cdb_iter_op cdb_op, - confd_value_t *oldv, confd_value_t *newv, void *args) -{ - char xpath[XPATH_MAXLEN]; - struct nb_node *nb_node; - enum nb_operation nb_op; - struct cdb_iter_args *iter_args = args; - char value_str[YANG_VALUE_MAXLEN]; - struct yang_data *data; - char *sb1, *sb2; - int ret; - - frr_confd_get_xpath(kp, xpath, sizeof(xpath)); - - /* - * HACK: obtain value of leaf-list elements from the XPath due to - * a bug in the ConfD API. - */ - value_str[0] = '\0'; - sb1 = strrchr(xpath, '['); - sb2 = strrchr(xpath, ']'); - if (sb1 && sb2 && !strchr(sb1, '=')) { - *sb2 = '\0'; - strlcpy(value_str, sb1 + 1, sizeof(value_str)); - *sb1 = '\0'; - } - - nb_node = nb_node_find(xpath); - if (!nb_node) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, xpath); - iter_args->error = true; - return ITER_STOP; - } - - /* Map operation values. */ - switch (cdb_op) { - case MOP_CREATED: - nb_op = NB_OP_CREATE; - break; - case MOP_DELETED: - nb_op = NB_OP_DESTROY; - break; - case MOP_VALUE_SET: - if (nb_is_operation_allowed(nb_node, NB_OP_MODIFY)) - nb_op = NB_OP_MODIFY; - else - /* Ignore list keys modifications. */ - return ITER_RECURSE; - break; - case MOP_MOVED_AFTER: - nb_op = NB_OP_MOVE; - break; - case MOP_MODIFIED: - /* We're not interested on this. */ - return ITER_RECURSE; - default: - flog_err(EC_LIB_DEVELOPMENT, - "%s: unexpected operation %u [xpath %s]", __func__, - cdb_op, xpath); - iter_args->error = true; - return ITER_STOP; - } - - /* Convert ConfD value to a string. */ - if (nb_node->snode->nodetype != LYS_LEAFLIST && newv - && frr_confd_val2str(nb_node->xpath, newv, value_str, - sizeof(value_str)) - != 0) { - flog_err(EC_LIB_CONFD_DATA_CONVERT, - "%s: failed to convert ConfD value to a string", - __func__); - iter_args->error = true; - return ITER_STOP; - } - - /* Edit the candidate configuration. */ - data = yang_data_new(xpath, value_str); - ret = nb_candidate_edit(iter_args->candidate, nb_node, nb_op, xpath, - NULL, data); - yang_data_free(data); - if (ret != NB_OK) { - flog_warn( - EC_LIB_NB_CANDIDATE_EDIT_ERROR, - "%s: failed to edit candidate configuration: operation [%s] xpath [%s]", - __func__, nb_operation_name(nb_op), xpath); - iter_args->error = true; - return ITER_STOP; - } - - return ITER_RECURSE; -} - -static void frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen) -{ - struct nb_context context = {}; - struct nb_config *candidate; - struct cdb_iter_args iter_args; - char errmsg[BUFSIZ] = {0}; - int ret; - - candidate = nb_config_dup(running_config); - - /* Iterate over all configuration changes. */ - iter_args.candidate = candidate; - iter_args.error = false; - for (int i = 0; i < reslen; i++) { - if (cdb_diff_iterate(fd, subp[i], frr_confd_cdb_diff_iter, - ITER_WANT_PREV, &iter_args) - != CONFD_OK) { - flog_err_confd("cdb_diff_iterate"); - } - } - free(subp); - - if (iter_args.error) { - nb_config_free(candidate); - - if (cdb_sub_abort_trans( - cdb_sub_sock, CONFD_ERRCODE_APPLICATION_INTERNAL, 0, - 0, "Couldn't apply configuration changes") - != CONFD_OK) { - flog_err_confd("cdb_sub_abort_trans"); - return; - } - return; - } - - /* - * Validate the configuration changes and allocate all resources - * required to apply them. - */ - transaction = NULL; - context.client = NB_CLIENT_CONFD; - ret = nb_candidate_commit_prepare(context, candidate, NULL, - &transaction, false, false, errmsg, - sizeof(errmsg)); - if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) { - enum confd_errcode errcode; - - switch (ret) { - case NB_ERR_LOCKED: - errcode = CONFD_ERRCODE_IN_USE; - break; - case NB_ERR_RESOURCE: - errcode = CONFD_ERRCODE_RESOURCE_DENIED; - break; - default: - errcode = CONFD_ERRCODE_APPLICATION; - break; - } - - /* Reject the configuration changes. */ - if (cdb_sub_abort_trans(cdb_sub_sock, errcode, 0, 0, "%s", - errmsg) - != CONFD_OK) { - flog_err_confd("cdb_sub_abort_trans"); - return; - } - } else { - /* Acknowledge the notification. */ - if (cdb_sync_subscription_socket(fd, CDB_DONE_PRIORITY) - != CONFD_OK) { - flog_err_confd("cdb_sync_subscription_socket"); - return; - } - - /* No configuration changes. */ - if (!transaction) - nb_config_free(candidate); - } -} - -static void frr_confd_cdb_read_cb_commit(int fd, int *subp, int reslen) -{ - /* - * No need to process the configuration changes again as we're already - * keeping track of them in the "transaction" variable. - */ - free(subp); - - /* Apply the transaction. */ - if (transaction) { - struct nb_config *candidate = transaction->config; - char errmsg[BUFSIZ] = {0}; - - nb_candidate_commit_apply(transaction, true, NULL, errmsg, - sizeof(errmsg)); - nb_config_free(candidate); - } - - /* Acknowledge the notification. */ - if (cdb_sync_subscription_socket(fd, CDB_DONE_PRIORITY) != CONFD_OK) { - flog_err_confd("cdb_sync_subscription_socket"); - return; - } -} - -static int frr_confd_cdb_read_cb_abort(int fd, int *subp, int reslen) -{ - /* - * No need to process the configuration changes again as we're already - * keeping track of them in the "transaction" variable. - */ - free(subp); - - /* Abort the transaction. */ - if (transaction) { - struct nb_config *candidate = transaction->config; - char errmsg[BUFSIZ] = {0}; - - nb_candidate_commit_abort(transaction, errmsg, sizeof(errmsg)); - nb_config_free(candidate); - } - - /* Acknowledge the notification. */ - if (cdb_sync_subscription_socket(fd, CDB_DONE_PRIORITY) != CONFD_OK) { - flog_err_confd("cdb_sync_subscription_socket"); - return -1; - } - - return 0; -} - -static void frr_confd_cdb_read_cb(struct event *thread) -{ - int fd = EVENT_FD(thread); - enum cdb_sub_notification cdb_ev; - int flags; - int *subp = NULL; - int reslen = 0; - - event_add_read(master, frr_confd_cdb_read_cb, NULL, fd, &t_cdb_sub); - - if (cdb_read_subscription_socket2(fd, &cdb_ev, &flags, &subp, &reslen) - != CONFD_OK) { - flog_err_confd("cdb_read_subscription_socket2"); - return; - } - - switch (cdb_ev) { - case CDB_SUB_PREPARE: - frr_confd_cdb_read_cb_prepare(fd, subp, reslen); - break; - case CDB_SUB_COMMIT: - frr_confd_cdb_read_cb_commit(fd, subp, reslen); - break; - case CDB_SUB_ABORT: - frr_confd_cdb_read_cb_abort(fd, subp, reslen); - break; - default: - flog_err_confd("unknown CDB event"); - break; - } -} - -/* Trigger CDB subscriptions to read the startup configuration. */ -static void *thread_cdb_trigger_subscriptions(void *data) -{ - int sock; - int *sub_points = NULL, len = 0; - struct listnode *node; - int *spoint; - int i = 0; - - /* Create CDB data socket. */ - sock = socket(PF_INET, SOCK_STREAM, 0); - if (sock < 0) { - flog_err(EC_LIB_SOCKET, "%s: failed to create socket: %s", - __func__, safe_strerror(errno)); - return NULL; - } - - if (cdb_connect(sock, CDB_DATA_SOCKET, &confd_addr, - sizeof(struct sockaddr_in)) - != CONFD_OK) { - flog_err_confd("cdb_connect"); - return NULL; - } - - /* - * Fill array containing the subscription point of all loaded YANG - * modules. - */ - len = listcount(confd_spoints); - sub_points = XCALLOC(MTYPE_CONFD, len * sizeof(int)); - for (ALL_LIST_ELEMENTS_RO(confd_spoints, node, spoint)) - sub_points[i++] = *spoint; - - if (cdb_trigger_subscriptions(sock, sub_points, len) != CONFD_OK) { - flog_err_confd("cdb_trigger_subscriptions"); - return NULL; - } - - /* Cleanup and exit thread. */ - XFREE(MTYPE_CONFD, sub_points); - cdb_close(sock); - - return NULL; -} - -static int frr_confd_subscribe(const struct lysc_node *snode, void *arg) -{ - struct yang_module *module = arg; - struct nb_node *nb_node; - int *spoint; - int ret; - - switch (snode->nodetype) { - case LYS_CONTAINER: - case LYS_LEAF: - case LYS_LEAFLIST: - case LYS_LIST: - break; - default: - return YANG_ITER_CONTINUE; - } - - if (CHECK_FLAG(snode->flags, LYS_CONFIG_R)) - return YANG_ITER_CONTINUE; - - nb_node = snode->priv; - if (!nb_node) - return YANG_ITER_CONTINUE; - - DEBUGD(&nb_dbg_client_confd, "%s: subscribing to '%s'", __func__, - nb_node->xpath); - - spoint = XMALLOC(MTYPE_CONFD, sizeof(*spoint)); - ret = cdb_subscribe2(cdb_sub_sock, CDB_SUB_RUNNING_TWOPHASE, - CDB_SUB_WANT_ABORT_ON_ABORT, 3, spoint, - module->confd_hash, nb_node->xpath); - if (ret != CONFD_OK) { - flog_err_confd("cdb_subscribe2"); - XFREE(MTYPE_CONFD, spoint); - return YANG_ITER_CONTINUE; - } - - listnode_add(confd_spoints, spoint); - return YANG_ITER_CONTINUE; -} - -static int frr_confd_init_cdb(void) -{ - struct yang_module *module; - pthread_t cdb_trigger_thread; - - /* Create CDB subscription socket. */ - cdb_sub_sock = socket(PF_INET, SOCK_STREAM, 0); - if (cdb_sub_sock < 0) { - flog_err(EC_LIB_SOCKET, "%s: failed to create socket: %s", - __func__, safe_strerror(errno)); - return -1; - } - - if (cdb_connect(cdb_sub_sock, CDB_SUBSCRIPTION_SOCKET, &confd_addr, - sizeof(struct sockaddr_in)) - != CONFD_OK) { - flog_err_confd("cdb_connect"); - goto error; - } - - /* Subscribe to all loaded YANG data modules. */ - confd_spoints = list_new(); - RB_FOREACH (module, yang_modules, &yang_modules) { - module->confd_hash = confd_str2hash(module->info->ns); - if (module->confd_hash == 0) { - flog_err( - EC_LIB_LIBCONFD, - "%s: failed to find hash value for namespace %s", - __func__, module->info->ns); - goto error; - } - - /* - * The CDB API doesn't provide a mechanism to subscribe to an - * entire YANG module. So we have to find the top level - * nodes ourselves and subscribe to their paths. - */ - yang_snodes_iterate(module->info, frr_confd_subscribe, 0, - module); - } - - if (cdb_subscribe_done(cdb_sub_sock) != CONFD_OK) { - flog_err_confd("cdb_subscribe_done"); - goto error; - } - - /* Create short lived pthread to trigger the CDB subscriptions. */ - if (pthread_create(&cdb_trigger_thread, NULL, - thread_cdb_trigger_subscriptions, NULL)) { - flog_err(EC_LIB_SYSTEM_CALL, "%s: error creating pthread: %s", - __func__, safe_strerror(errno)); - goto error; - } - pthread_detach(cdb_trigger_thread); - - event_add_read(master, frr_confd_cdb_read_cb, NULL, cdb_sub_sock, - &t_cdb_sub); - - return 0; - -error: - frr_confd_finish_cdb(); - - return -1; -} - -static void frr_confd_finish_cdb(void) -{ - if (cdb_sub_sock > 0) { - EVENT_OFF(t_cdb_sub); - cdb_close(cdb_sub_sock); - } -} - -/* ------------ DP code ------------ */ - -static int frr_confd_transaction_init(struct confd_trans_ctx *tctx) -{ - confd_trans_set_fd(tctx, dp_worker_sock); - - return CONFD_OK; -} - -#define CONFD_MAX_CHILD_NODES 32 - -static int frr_confd_data_get_elem(struct confd_trans_ctx *tctx, - confd_hkeypath_t *kp) -{ - struct nb_node *nb_node; - char xpath[XPATH_MAXLEN]; - struct yang_data *data; - confd_value_t v; - const void *list_entry = NULL; - - frr_confd_get_xpath(kp, xpath, sizeof(xpath)); - - nb_node = nb_node_find(xpath); - if (!nb_node) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, xpath); - confd_data_reply_not_found(tctx); - return CONFD_OK; - } - - if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &list_entry) != 0) { - confd_data_reply_not_found(tctx); - return CONFD_OK; - } - - data = nb_callback_get_elem(nb_node, xpath, list_entry); - if (data) { - if (data->value) { - CONFD_SET_STR(&v, data->value); - confd_data_reply_value(tctx, &v); - } else - confd_data_reply_found(tctx); - yang_data_free(data); - } else - confd_data_reply_not_found(tctx); - - return CONFD_OK; -} - -static int frr_confd_data_get_next(struct confd_trans_ctx *tctx, - confd_hkeypath_t *kp, long next) -{ - struct nb_node *nb_node; - char xpath[XPATH_MAXLEN]; - struct yang_data *data; - const void *parent_list_entry, *nb_next; - confd_value_t v[LIST_MAXKEYS]; - - frr_confd_get_xpath(kp, xpath, sizeof(xpath)); - - nb_node = nb_node_find(xpath); - if (!nb_node) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, xpath); - confd_data_reply_next_key(tctx, NULL, -1, -1); - return CONFD_OK; - } - - if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &parent_list_entry) - != 0) { - /* List entry doesn't exist anymore. */ - confd_data_reply_next_key(tctx, NULL, -1, -1); - return CONFD_OK; - } - - nb_next = nb_callback_get_next(nb_node, parent_list_entry, - (next == -1) ? NULL : (void *)next); - if (!nb_next) { - /* End of the list or leaf-list. */ - confd_data_reply_next_key(tctx, NULL, -1, -1); - return CONFD_OK; - } - - switch (nb_node->snode->nodetype) { - case LYS_LIST: - if (!CHECK_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST)) { - struct yang_list_keys keys; - - memset(&keys, 0, sizeof(keys)); - if (nb_callback_get_keys(nb_node, nb_next, &keys) - != NB_OK) { - flog_warn(EC_LIB_NB_CB_STATE, - "%s: failed to get list keys", - __func__); - confd_data_reply_next_key(tctx, NULL, -1, -1); - return CONFD_OK; - } - - /* Feed keys to ConfD. */ - for (size_t i = 0; i < keys.num; i++) - CONFD_SET_STR(&v[i], keys.key[i]); - confd_data_reply_next_key(tctx, v, keys.num, - (long)nb_next); - } else { - char pointer_str[32]; - - /* - * ConfD 6.6 user guide, chapter 6.11 (Operational data - * lists without keys): - * "To support this without having completely separate - * APIs, we use a "pseudo" key in the ConfD APIs for - * this type of list. This key is not part of the data - * model, and completely hidden in the northbound agent - * interfaces, but is used with e.g. the get_next() and - * get_elem() callbacks as if it were a normal key. This - * "pseudo" key is always a single signed 64-bit - * integer, i.e. the confd_value_t type is C_INT64. The - * values can be chosen arbitrarily by the application, - * as long as a key value returned by get_next() can be - * used to get the data for the corresponding list entry - * with get_elem() or get_object() as usual. It could - * e.g. be an index into an array that holds the data, - * or even a memory address in integer form". - * - * Since we're using the CONFD_DAEMON_FLAG_STRINGSONLY - * option, we must convert our pseudo-key (a void - * pointer) to a string before sending it to confd. - */ - snprintf(pointer_str, sizeof(pointer_str), "%lu", - (unsigned long)nb_next); - CONFD_SET_STR(&v[0], pointer_str); - confd_data_reply_next_key(tctx, v, 1, (long)nb_next); - } - break; - case LYS_LEAFLIST: - data = nb_callback_get_elem(nb_node, xpath, nb_next); - if (data) { - if (data->value) { - CONFD_SET_STR(&v[0], data->value); - confd_data_reply_next_key(tctx, v, 1, - (long)nb_next); - } - yang_data_free(data); - } else - confd_data_reply_next_key(tctx, NULL, -1, -1); - break; - default: - break; - } - - return CONFD_OK; -} - -/* - * Optional callback - implemented for performance reasons. - */ -static int frr_confd_data_get_object(struct confd_trans_ctx *tctx, - confd_hkeypath_t *kp) -{ - struct nb_node *nb_node; - const struct lysc_node *child; - char xpath[XPATH_MAXLEN]; - char xpath_child[XPATH_MAXLEN * 2]; - struct list *elements; - struct yang_data *data; - const void *list_entry; - confd_value_t values[CONFD_MAX_CHILD_NODES]; - size_t nvalues = 0; - - frr_confd_get_xpath(kp, xpath, sizeof(xpath)); - - nb_node = nb_node_find(xpath); - if (!nb_node) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, xpath); - confd_data_reply_not_found(tctx); - return CONFD_ERR; - } - - if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &list_entry) != 0) { - confd_data_reply_not_found(tctx); - return CONFD_OK; - } - - elements = yang_data_list_new(); - - /* Loop through list child nodes. */ - LY_LIST_FOR (lysc_node_child(nb_node->snode), child) { - struct nb_node *nb_node_child = child->priv; - confd_value_t *v; - - if (nvalues > CONFD_MAX_CHILD_NODES) - break; - - v = &values[nvalues++]; - - /* Non-presence containers, lists and leaf-lists. */ - if (!nb_node_child->cbs.get_elem) { - CONFD_SET_NOEXISTS(v); - continue; - } - - snprintf(xpath_child, sizeof(xpath_child), "%s/%s", xpath, - child->name); - data = nb_callback_get_elem(nb_node_child, xpath_child, - list_entry); - if (data) { - if (data->value) - CONFD_SET_STR(v, data->value); - else { - /* Presence containers and empty leafs. */ - CONFD_SET_XMLTAG( - v, nb_node_child->confd_hash, - confd_str2hash(nb_node_child->snode - ->module->ns)); - } - listnode_add(elements, data); - } else - CONFD_SET_NOEXISTS(v); - } - - confd_data_reply_value_array(tctx, values, nvalues); - - /* Release memory. */ - list_delete(&elements); - - return CONFD_OK; -} - -/* - * Optional callback - implemented for performance reasons. - */ -static int frr_confd_data_get_next_object(struct confd_trans_ctx *tctx, - confd_hkeypath_t *kp, long next) -{ - char xpath[XPATH_MAXLEN]; - struct nb_node *nb_node; - struct list *elements; - const void *parent_list_entry; - const void *nb_next; -#define CONFD_OBJECTS_PER_TIME 100 - struct confd_next_object objects[CONFD_OBJECTS_PER_TIME + 1]; - char pseudo_keys[CONFD_OBJECTS_PER_TIME][32]; - int nobjects = 0; - - frr_confd_get_xpath(kp, xpath, sizeof(xpath)); - - nb_node = nb_node_find(xpath); - if (!nb_node) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, xpath); - confd_data_reply_next_object_array(tctx, NULL, 0, 0); - return CONFD_OK; - } - - if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &parent_list_entry) - != 0) { - confd_data_reply_next_object_array(tctx, NULL, 0, 0); - return CONFD_OK; - } - - elements = yang_data_list_new(); - nb_next = (next == -1) ? NULL : (void *)next; - - memset(objects, 0, sizeof(objects)); - for (int j = 0; j < CONFD_OBJECTS_PER_TIME; j++) { - struct confd_next_object *object; - const struct lysc_node *child; - struct yang_data *data; - size_t nvalues = 0; - - object = &objects[j]; - - nb_next = nb_callback_get_next(nb_node, parent_list_entry, - nb_next); - if (!nb_next) - /* End of the list. */ - break; - - object->next = (long)nb_next; - - /* Leaf-lists require special handling. */ - if (nb_node->snode->nodetype == LYS_LEAFLIST) { - object->v = XMALLOC(MTYPE_CONFD, sizeof(confd_value_t)); - data = nb_callback_get_elem(nb_node, xpath, nb_next); - assert(data && data->value); - CONFD_SET_STR(object->v, data->value); - nvalues++; - listnode_add(elements, data); - goto next; - } - - object->v = - XMALLOC(MTYPE_CONFD, - CONFD_MAX_CHILD_NODES * sizeof(confd_value_t)); - - /* - * ConfD 6.6 user guide, chapter 6.11 (Operational data lists - * without keys): - * "In the response to the get_next_object() callback, the data - * provider is expected to provide the key values along with the - * other leafs in an array that is populated according to the - * data model. This must be done also for this type of list, - * even though the key isn't actually in the data model. The - * "pseudo" key must always be the first element in the array". - */ - if (CHECK_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST)) { - confd_value_t *v; - - snprintf(pseudo_keys[j], sizeof(pseudo_keys[j]), "%lu", - (unsigned long)nb_next); - - v = &object->v[nvalues++]; - CONFD_SET_STR(v, pseudo_keys[j]); - } - - /* Loop through list child nodes. */ - LY_LIST_FOR (lysc_node_child(nb_node->snode), child) { - struct nb_node *nb_node_child = child->priv; - char xpath_child[XPATH_MAXLEN * 2]; - confd_value_t *v; - - if (nvalues > CONFD_MAX_CHILD_NODES) - break; - - v = &object->v[nvalues++]; - - /* Non-presence containers, lists and leaf-lists. */ - if (!nb_node_child->cbs.get_elem) { - CONFD_SET_NOEXISTS(v); - continue; - } - - snprintf(xpath_child, sizeof(xpath_child), "%s/%s", - xpath, child->name); - data = nb_callback_get_elem(nb_node_child, xpath_child, - nb_next); - if (data) { - if (data->value) - CONFD_SET_STR(v, data->value); - else { - /* - * Presence containers and empty leafs. - */ - CONFD_SET_XMLTAG( - v, nb_node_child->confd_hash, - confd_str2hash( - nb_node_child->snode - ->module->ns)); - } - listnode_add(elements, data); - } else - CONFD_SET_NOEXISTS(v); - } - next: - object->n = nvalues; - nobjects++; - } - - if (nobjects == 0) { - confd_data_reply_next_object_array(tctx, NULL, 0, 0); - list_delete(&elements); - return CONFD_OK; - } - - /* Detect end of the list. */ - if (!nb_next) { - nobjects++; - objects[nobjects].v = NULL; - } - - /* Reply to ConfD. */ - confd_data_reply_next_object_arrays(tctx, objects, nobjects, 0); - if (!nb_next) - nobjects--; - - /* Release memory. */ - list_delete(&elements); - for (int j = 0; j < nobjects; j++) { - struct confd_next_object *object; - - object = &objects[j]; - XFREE(MTYPE_CONFD, object->v); - } - - return CONFD_OK; -} - -static int frr_confd_notification_send(const char *xpath, - struct list *arguments) -{ - struct nb_node *nb_node; - struct yang_module *module; - struct confd_datetime now; - confd_tag_value_t *values; - int nvalues; - int i = 0; - struct yang_data *data; - struct listnode *node; - int ret; - - nb_node = nb_node_find(xpath); - if (!nb_node) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, xpath); - return -1; - } - module = yang_module_find(nb_node->snode->module->name); - assert(module); - - nvalues = 2; - if (arguments) - nvalues += listcount(arguments); - - values = XMALLOC(MTYPE_CONFD, nvalues * sizeof(*values)); - - CONFD_SET_TAG_XMLBEGIN(&values[i++], nb_node->confd_hash, - module->confd_hash); - for (ALL_LIST_ELEMENTS_RO(arguments, node, data)) { - struct nb_node *nb_node_arg; - - nb_node_arg = nb_node_find(data->xpath); - if (!nb_node_arg) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, - data->xpath); - XFREE(MTYPE_CONFD, values); - return NB_ERR; - } - - CONFD_SET_TAG_STR(&values[i++], nb_node_arg->confd_hash, - data->value); - } - CONFD_SET_TAG_XMLEND(&values[i++], nb_node->confd_hash, - module->confd_hash); - - getdatetime(&now); - ret = confd_notification_send(live_ctx, &now, values, nvalues); - - /* Release memory. */ - XFREE(MTYPE_CONFD, values); - - /* Map ConfD return code to northbound return code. */ - switch (ret) { - case CONFD_OK: - return NB_OK; - default: - return NB_ERR; - } -} - -static int frr_confd_action_init(struct confd_user_info *uinfo) -{ - confd_action_set_fd(uinfo, dp_worker_sock); - - return CONFD_OK; -} - -static int frr_confd_action_execute(struct confd_user_info *uinfo, - struct xml_tag *name, confd_hkeypath_t *kp, - confd_tag_value_t *params, int nparams) -{ - char xpath[XPATH_MAXLEN]; - struct nb_node *nb_node; - struct list *input; - struct list *output; - struct yang_data *data; - confd_tag_value_t *reply; - int ret = CONFD_OK; - char errmsg[BUFSIZ] = {0}; - - /* Getting the XPath is tricky. */ - if (kp) { - /* This is a YANG RPC. */ - frr_confd_get_xpath(kp, xpath, sizeof(xpath)); - strlcat(xpath, "/", sizeof(xpath)); - strlcat(xpath, confd_hash2str(name->tag), sizeof(xpath)); - } else { - /* This is a YANG action. */ - snprintf(xpath, sizeof(xpath), "/%s:%s", - confd_ns2prefix(name->ns), confd_hash2str(name->tag)); - } - - nb_node = nb_node_find(xpath); - if (!nb_node) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, xpath); - return CONFD_ERR; - } - - input = yang_data_list_new(); - output = yang_data_list_new(); - - /* Process input nodes. */ - for (int i = 0; i < nparams; i++) { - char xpath_input[XPATH_MAXLEN * 2]; - char value_str[YANG_VALUE_MAXLEN]; - - snprintf(xpath_input, sizeof(xpath_input), "%s/%s", xpath, - confd_hash2str(params[i].tag.tag)); - - if (frr_confd_val2str(xpath_input, ¶ms[i].v, value_str, - sizeof(value_str)) - != 0) { - flog_err( - EC_LIB_CONFD_DATA_CONVERT, - "%s: failed to convert ConfD value to a string", - __func__); - ret = CONFD_ERR; - goto exit; - } - - data = yang_data_new(xpath_input, value_str); - listnode_add(input, data); - } - - /* Execute callback registered for this XPath. */ - if (nb_callback_rpc(nb_node, xpath, input, output, errmsg, - sizeof(errmsg)) - != NB_OK) { - flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s", - __func__, xpath); - ret = CONFD_ERR; - goto exit; - } - - /* Process output nodes. */ - if (listcount(output) > 0) { - struct listnode *node; - int i = 0; - - reply = XMALLOC(MTYPE_CONFD, - listcount(output) * sizeof(*reply)); - - for (ALL_LIST_ELEMENTS_RO(output, node, data)) { - struct nb_node *nb_node_output; - int hash; - - nb_node_output = nb_node_find(data->xpath); - if (!nb_node_output) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, - data->xpath); - goto exit; - } - - hash = confd_str2hash(nb_node_output->snode->name); - CONFD_SET_TAG_STR(&reply[i++], hash, data->value); - } - confd_action_reply_values(uinfo, reply, listcount(output)); - XFREE(MTYPE_CONFD, reply); - } - -exit: - /* Release memory. */ - list_delete(&input); - list_delete(&output); - - return ret; -} - - -static int frr_confd_dp_read(struct confd_daemon_ctx *dctx, int fd) -{ - int ret; - - ret = confd_fd_ready(dctx, fd); - if (ret == CONFD_EOF) { - flog_err_confd("confd_fd_ready"); - frr_confd_finish(); - return -1; - } else if (ret == CONFD_ERR && confd_errno != CONFD_ERR_EXTERNAL) { - flog_err_confd("confd_fd_ready"); - frr_confd_finish(); - return -1; - } - - return 0; -} - -static void frr_confd_dp_ctl_read(struct event *thread) -{ - struct confd_daemon_ctx *dctx = EVENT_ARG(thread); - int fd = EVENT_FD(thread); - - event_add_read(master, frr_confd_dp_ctl_read, dctx, fd, &t_dp_ctl); - - frr_confd_dp_read(dctx, fd); -} - -static void frr_confd_dp_worker_read(struct event *thread) -{ - struct confd_daemon_ctx *dctx = EVENT_ARG(thread); - int fd = EVENT_FD(thread); - - event_add_read(master, frr_confd_dp_worker_read, dctx, fd, - &t_dp_worker); - - frr_confd_dp_read(dctx, fd); -} - -static int frr_confd_subscribe_state(const struct lysc_node *snode, void *arg) -{ - struct nb_node *nb_node = snode->priv; - struct confd_data_cbs *data_cbs = arg; - - if (!nb_node || !CHECK_FLAG(snode->flags, LYS_CONFIG_R)) - return YANG_ITER_CONTINUE; - /* We only need to subscribe to the root of the state subtrees. */ - if (snode->parent && CHECK_FLAG(snode->parent->flags, LYS_CONFIG_R)) - return YANG_ITER_CONTINUE; - - DEBUGD(&nb_dbg_client_confd, - "%s: providing data to '%s' (callpoint %s)", __func__, - nb_node->xpath, snode->name); - - strlcpy(data_cbs->callpoint, snode->name, sizeof(data_cbs->callpoint)); - if (confd_register_data_cb(dctx, data_cbs) != CONFD_OK) - flog_err_confd("confd_register_data_cb"); - - return YANG_ITER_CONTINUE; -} - -static int frr_confd_init_dp(const char *program_name) -{ - struct confd_trans_cbs trans_cbs; - struct confd_data_cbs data_cbs; - struct confd_notification_stream_cbs ncbs; - struct confd_action_cbs acbs; - - /* Initialize daemon context. */ - dctx = confd_init_daemon(program_name); - if (!dctx) { - flog_err_confd("confd_init_daemon"); - goto error; - } - - /* - * Inform we want to receive YANG values as raw strings, and that we - * want to provide only strings in the reply functions, regardless of - * the YANG type. - */ - confd_set_daemon_flags(dctx, CONFD_DAEMON_FLAG_STRINGSONLY); - - /* Establish a control socket. */ - dp_ctl_sock = socket(PF_INET, SOCK_STREAM, 0); - if (dp_ctl_sock < 0) { - flog_err(EC_LIB_SOCKET, "%s: failed to create socket: %s", - __func__, safe_strerror(errno)); - goto error; - } - - if (confd_connect(dctx, dp_ctl_sock, CONTROL_SOCKET, &confd_addr, - sizeof(struct sockaddr_in)) - != CONFD_OK) { - flog_err_confd("confd_connect"); - goto error; - } - - /* - * Establish a worker socket (only one since this plugin runs on a - * single thread). - */ - dp_worker_sock = socket(PF_INET, SOCK_STREAM, 0); - if (dp_worker_sock < 0) { - flog_err(EC_LIB_SOCKET, "%s: failed to create socket: %s", - __func__, safe_strerror(errno)); - goto error; - } - if (confd_connect(dctx, dp_worker_sock, WORKER_SOCKET, &confd_addr, - sizeof(struct sockaddr_in)) - != CONFD_OK) { - flog_err_confd("confd_connect"); - goto error; - } - - /* Register transaction callback functions. */ - memset(&trans_cbs, 0, sizeof(trans_cbs)); - trans_cbs.init = frr_confd_transaction_init; - confd_register_trans_cb(dctx, &trans_cbs); - - /* Register our read/write callbacks. */ - memset(&data_cbs, 0, sizeof(data_cbs)); - data_cbs.get_elem = frr_confd_data_get_elem; - data_cbs.exists_optional = frr_confd_data_get_elem; - data_cbs.get_next = frr_confd_data_get_next; - data_cbs.get_object = frr_confd_data_get_object; - data_cbs.get_next_object = frr_confd_data_get_next_object; - - /* - * Iterate over all loaded YANG modules and subscribe to the paths - * referent to state data. - */ - yang_snodes_iterate(NULL, frr_confd_subscribe_state, 0, &data_cbs); - - /* Register notification stream. */ - memset(&ncbs, 0, sizeof(ncbs)); - ncbs.fd = dp_worker_sock; - /* - * RFC 5277 - Section 3.2.3: - * A NETCONF server implementation supporting the notification - * capability MUST support the "NETCONF" notification event - * stream. This stream contains all NETCONF XML event notifications - * supported by the NETCONF server. - */ - strlcpy(ncbs.streamname, "NETCONF", sizeof(ncbs.streamname)); - if (confd_register_notification_stream(dctx, &ncbs, &live_ctx) - != CONFD_OK) { - flog_err_confd("confd_register_notification_stream"); - goto error; - } - - /* Register the action handler callback. */ - memset(&acbs, 0, sizeof(acbs)); - strlcpy(acbs.actionpoint, "actionpoint", sizeof(acbs.actionpoint)); - acbs.init = frr_confd_action_init; - acbs.action = frr_confd_action_execute; - if (confd_register_action_cbs(dctx, &acbs) != CONFD_OK) { - flog_err_confd("confd_register_action_cbs"); - goto error; - } - - /* Notify we registered all callbacks we wanted. */ - if (confd_register_done(dctx) != CONFD_OK) { - flog_err_confd("confd_register_done"); - goto error; - } - - event_add_read(master, frr_confd_dp_ctl_read, dctx, dp_ctl_sock, - &t_dp_ctl); - event_add_read(master, frr_confd_dp_worker_read, dctx, dp_worker_sock, - &t_dp_worker); - - return 0; - -error: - frr_confd_finish_dp(); - - return -1; -} - -static void frr_confd_finish_dp(void) -{ - if (dp_worker_sock > 0) { - EVENT_OFF(t_dp_worker); - close(dp_worker_sock); - } - if (dp_ctl_sock > 0) { - EVENT_OFF(t_dp_ctl); - close(dp_ctl_sock); - } - if (dctx != NULL) - confd_release_daemon(dctx); -} - -/* ------------ CLI ------------ */ - -DEFUN (debug_nb_confd, - debug_nb_confd_cmd, - "[no] debug northbound client confd", - NO_STR - DEBUG_STR - "Northbound debugging\n" - "Client\n" - "ConfD\n") -{ - uint32_t mode = DEBUG_NODE2MODE(vty->node); - bool no = strmatch(argv[0]->text, "no"); - - DEBUG_MODE_SET(&nb_dbg_client_confd, mode, !no); - - return CMD_SUCCESS; -} - -static int frr_confd_debug_config_write(struct vty *vty) -{ - if (DEBUG_MODE_CHECK(&nb_dbg_client_confd, DEBUG_MODE_CONF)) - vty_out(vty, "debug northbound client confd\n"); - - return 0; -} - -static int frr_confd_debug_set_all(uint32_t flags, bool set) -{ - DEBUG_FLAGS_SET(&nb_dbg_client_confd, flags, set); - - /* If all modes have been turned off, don't preserve options. */ - if (!DEBUG_MODE_CHECK(&nb_dbg_client_confd, DEBUG_MODE_ALL)) - DEBUG_CLEAR(&nb_dbg_client_confd); - - return 0; -} - -static void frr_confd_cli_init(void) -{ - hook_register(nb_client_debug_config_write, - frr_confd_debug_config_write); - hook_register(nb_client_debug_set_all, frr_confd_debug_set_all); - - install_element(ENABLE_NODE, &debug_nb_confd_cmd); - install_element(CONFIG_NODE, &debug_nb_confd_cmd); -} - -/* ------------ Main ------------ */ - -static int frr_confd_calculate_snode_hash(const struct lysc_node *snode, - void *arg) -{ - struct nb_node *nb_node = snode->priv; - - if (nb_node) - nb_node->confd_hash = confd_str2hash(snode->name); - - return YANG_ITER_CONTINUE; -} - -static int frr_confd_init(const char *program_name) -{ - struct sockaddr_in *confd_addr4 = (struct sockaddr_in *)&confd_addr; - int debuglevel = CONFD_SILENT; - int ret = -1; - - /* Initialize ConfD library. */ - confd_init(program_name, stderr, debuglevel); - - confd_addr4->sin_family = AF_INET; - confd_addr4->sin_addr.s_addr = inet_addr("127.0.0.1"); - confd_addr4->sin_port = htons(CONFD_PORT); - if (confd_load_schemas(&confd_addr, sizeof(struct sockaddr_in)) - != CONFD_OK) { - flog_err_confd("confd_load_schemas"); - return -1; - } - - ret = frr_confd_init_cdb(); - if (ret != 0) - goto error; - - ret = frr_confd_init_dp(program_name); - if (ret != 0) { - frr_confd_finish_cdb(); - goto error; - } - - yang_snodes_iterate(NULL, frr_confd_calculate_snode_hash, 0, NULL); - - hook_register(nb_notification_send, frr_confd_notification_send); - - confd_connected = true; - return 0; - -error: - confd_free_schemas(); - - return ret; -} - -static int frr_confd_finish(void) -{ - if (!confd_connected) - return 0; - - frr_confd_finish_cdb(); - frr_confd_finish_dp(); - - confd_free_schemas(); - - confd_connected = false; - - return 0; -} - -static int frr_confd_module_late_init(struct event_loop *tm) -{ - master = tm; - - if (frr_confd_init(frr_get_progname()) < 0) { - flog_err(EC_LIB_CONFD_INIT, - "failed to initialize the ConfD module"); - return -1; - } - - hook_register(frr_fini, frr_confd_finish); - frr_confd_cli_init(); - - return 0; -} - -static int frr_confd_module_init(void) -{ - hook_register(frr_late_init, frr_confd_module_late_init); - - return 0; -} - -FRR_MODULE_SETUP(.name = "frr_confd", .version = FRR_VERSION, - .description = "FRR ConfD integration module", - .init = frr_confd_module_init, -); diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp index 7957752..612ec39 100644 --- a/lib/northbound_grpc.cpp +++ b/lib/northbound_grpc.cpp @@ -1011,12 +1011,11 @@ grpc::Status HandleUnaryExecute( grpc_debug("%s: entered", __func__); struct nb_node *nb_node; - struct list *input_list; - struct list *output_list; - struct listnode *node; - struct yang_data *data; + struct lyd_node *input_tree, *output_tree, *child; const char *xpath; char errmsg[BUFSIZ] = {0}; + char path[XPATH_MAXLEN]; + LY_ERR err; // Request: string path = 1; xpath = tag->request.path().c_str(); @@ -1032,40 +1031,66 @@ grpc::Status HandleUnaryExecute( return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, "Unknown data path"); - input_list = yang_data_list_new(); - output_list = yang_data_list_new(); + // Create input data tree. + err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, + (LYD_ANYDATA_VALUETYPE)0, 0, NULL, &input_tree); + if (err != LY_SUCCESS) { + return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, + "Invalid data path"); + } // Read input parameters. auto input = tag->request.input(); for (const frr::PathValue &pv : input) { // Request: repeated PathValue input = 2; - data = yang_data_new(pv.path().c_str(), pv.value().c_str()); - listnode_add(input_list, data); + err = lyd_new_path(input_tree, ly_native_ctx, pv.path().c_str(), + pv.value().c_str(), 0, NULL); + if (err != LY_SUCCESS) { + lyd_free_tree(input_tree); + return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, + "Invalid input data"); + } + } + + // Validate input data. + err = lyd_validate_op(input_tree, NULL, LYD_TYPE_RPC_YANG, NULL); + if (err != LY_SUCCESS) { + lyd_free_tree(input_tree); + return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, + "Invalid input data"); + } + + // Create output data tree. + err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, + (LYD_ANYDATA_VALUETYPE)0, 0, NULL, &output_tree); + if (err != LY_SUCCESS) { + lyd_free_tree(input_tree); + return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, + "Invalid data path"); } // Execute callback registered for this XPath. - if (nb_callback_rpc(nb_node, xpath, input_list, output_list, errmsg, - sizeof(errmsg)) - != NB_OK) { + if (nb_callback_rpc(nb_node, xpath, input_tree, output_tree, errmsg, + sizeof(errmsg)) != NB_OK) { flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s", __func__, xpath); - list_delete(&input_list); - list_delete(&output_list); + lyd_free_tree(input_tree); + lyd_free_tree(output_tree); return grpc::Status(grpc::StatusCode::INTERNAL, "RPC failed"); } // Process output parameters. - for (ALL_LIST_ELEMENTS_RO(output_list, node, data)) { + LY_LIST_FOR (lyd_child(output_tree), child) { // Response: repeated PathValue output = 1; frr::PathValue *pv = tag->response.add_output(); - pv->set_path(data->xpath); - pv->set_value(data->value); + pv->set_path(lyd_path(child, LYD_PATH_STD, path, sizeof(path))); + pv->set_value(yang_dnode_get_string(child, NULL)); } // Release memory. - list_delete(&input_list); - list_delete(&output_list); + lyd_free_tree(input_tree); + lyd_free_tree(output_tree); return grpc::Status::OK; } diff --git a/lib/northbound_oper.c b/lib/northbound_oper.c index 2394b5e..5f38c97 100644 --- a/lib/northbound_oper.c +++ b/lib/northbound_oper.c @@ -342,38 +342,6 @@ static void nb_op_resume_data_tree(struct nb_op_yield_state *ys) /* ======================= */ /** - * __xpath_pop_node() - remove the last node from xpath string - * @xpath: an xpath string - * - * Return: NB_OK or NB_ERR_NOT_FOUND if nothing left to pop. - */ -static int __xpath_pop_node(char *xpath) -{ - int len = strlen(xpath); - bool abs = xpath[0] == '/'; - char *slash; - - /* "//" or "/" => NULL */ - if (abs && (len == 1 || (len == 2 && xpath[1] == '/'))) - return NB_ERR_NOT_FOUND; - - slash = (char *)frrstr_back_to_char(xpath, '/'); - /* "/foo/bar/" or "/foo/bar//" => "/foo " */ - if (slash && slash == &xpath[len - 1]) { - xpath[--len] = 0; - slash = (char *)frrstr_back_to_char(xpath, '/'); - if (slash && slash == &xpath[len - 1]) { - xpath[--len] = 0; - slash = (char *)frrstr_back_to_char(xpath, '/'); - } - } - if (!slash) - return NB_ERR_NOT_FOUND; - *slash = 0; - return NB_OK; -} - -/** * nb_op_xpath_to_trunk() - generate a lyd_node tree (trunk) using an xpath. * @xpath_in: xpath query string to build trunk from. * @dnode: resulting tree (trunk) @@ -398,7 +366,7 @@ static enum nb_error nb_op_xpath_to_trunk(const char *xpath_in, if (err == LY_SUCCESS) break; - ret = __xpath_pop_node(xpath); + ret = yang_xpath_pop_node(xpath); if (ret != NB_OK) break; } @@ -1588,8 +1556,9 @@ static enum nb_error nb_op_yield(struct nb_op_yield_state *ys) unsigned long min_us = MAX(1, NB_OP_WALK_INTERVAL_US / 50000); struct timeval tv = { .tv_sec = 0, .tv_usec = min_us }; - DEBUGD(&nb_dbg_events, "NB oper-state: yielding %s for %lus (should_batch %d)", - ys->xpath, tv.tv_usec, ys->should_batch); + DEBUGD(&nb_dbg_events, + "NB oper-state: yielding %s for %lldus (should_batch %d)", + ys->xpath, (long long)tv.tv_usec, ys->should_batch); if (ys->should_batch) { /* diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c index 4942d66..0ec7610 100644 --- a/lib/northbound_sysrepo.c +++ b/lib/northbound_sysrepo.c @@ -19,8 +19,6 @@ #include <sysrepo/values.h> #include <sysrepo/xpath.h> -DEFINE_MTYPE_STATIC(LIB, SYSREPO, "Sysrepo module"); - static struct debug nb_dbg_client_sysrepo = {0, "Northbound client: Sysrepo"}; static struct event_loop *master; @@ -379,16 +377,11 @@ static int frr_sr_state_cb(sr_session_ctx_t *session, uint32_t sub_id, return SR_ERR_OK; } static int frr_sr_config_rpc_cb(sr_session_ctx_t *session, uint32_t sub_id, - const char *xpath, const sr_val_t *sr_input, - const size_t input_cnt, sr_event_t sr_ev, - uint32_t request_id, sr_val_t **sr_output, - size_t *sr_output_cnt, void *private_ctx) + const char *xpath, const struct lyd_node *input, + sr_event_t sr_ev, uint32_t request_id, + struct lyd_node *output, void *private_ctx) { struct nb_node *nb_node; - struct list *input; - struct list *output; - struct yang_data *data; - size_t cb_output_cnt; int ret = SR_ERR_OK; char errmsg[BUFSIZ] = {0}; @@ -399,19 +392,6 @@ static int frr_sr_config_rpc_cb(sr_session_ctx_t *session, uint32_t sub_id, return SR_ERR_INTERNAL; } - input = yang_data_list_new(); - output = yang_data_list_new(); - - /* Process input. */ - for (size_t i = 0; i < input_cnt; i++) { - char value_str[YANG_VALUE_MAXLEN]; - - sr_val_to_buff(&sr_input[i], value_str, sizeof(value_str)); - - data = yang_data_new(xpath, value_str); - listnode_add(input, data); - } - /* Execute callback registered for this XPath. */ if (nb_callback_rpc(nb_node, xpath, input, output, errmsg, sizeof(errmsg)) @@ -419,44 +399,8 @@ static int frr_sr_config_rpc_cb(sr_session_ctx_t *session, uint32_t sub_id, flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s", __func__, xpath); ret = SR_ERR_OPERATION_FAILED; - goto exit; - } - - /* Process output. */ - if (listcount(output) > 0) { - sr_val_t *values = NULL; - struct listnode *node; - int i = 0; - - cb_output_cnt = listcount(output); - ret = sr_new_values(cb_output_cnt, &values); - if (ret != SR_ERR_OK) { - flog_err(EC_LIB_LIBSYSREPO, "%s: sr_new_values(): %s", - __func__, sr_strerror(ret)); - goto exit; - } - - for (ALL_LIST_ELEMENTS_RO(output, node, data)) { - if (yang_data_frr2sr(data, &values[i++]) != 0) { - flog_err( - EC_LIB_SYSREPO_DATA_CONVERT, - "%s: failed to convert data to Sysrepo format", - __func__); - ret = SR_ERR_INTERNAL; - sr_free_values(values, cb_output_cnt); - goto exit; - } - } - - *sr_output = values; - *sr_output_cnt = cb_output_cnt; } -exit: - /* Release memory. */ - list_delete(&input); - list_delete(&output); - return ret; } @@ -581,8 +525,9 @@ static int frr_sr_subscribe_rpc(const struct lysc_node *snode, void *arg) DEBUGD(&nb_dbg_client_sysrepo, "sysrepo: providing RPC to '%s'", nb_node->xpath); - ret = sr_rpc_subscribe(session, nb_node->xpath, frr_sr_config_rpc_cb, - NULL, 0, 0, &module->sr_subscription); + ret = sr_rpc_subscribe_tree(session, nb_node->xpath, + frr_sr_config_rpc_cb, NULL, 0, 0, + &module->sr_subscription); if (ret != SR_ERR_OK) flog_err(EC_LIB_LIBSYSREPO, "sr_rpc_subscribe(): %s", sr_strerror(ret)); diff --git a/lib/plist.c b/lib/plist.c index 618d92b..2cfaa7d 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -1073,17 +1073,13 @@ static int vty_show_prefix_list(struct vty *vty, afi_t afi, const char *name, struct prefix_master *master; int64_t seqnum = 0; json_object *json = NULL; - json_object *json_proto = NULL; master = prefix_master_get(afi, 0); if (master == NULL) return CMD_WARNING; - if (uj) { + if (uj) json = json_object_new_object(); - json_proto = json_object_new_object(); - json_object_object_add(json, frr_protoname, json_proto); - } if (seq) seqnum = (int64_t)atol(seq); @@ -1096,8 +1092,8 @@ static int vty_show_prefix_list(struct vty *vty, afi_t afi, const char *name, "%% Can't find specified prefix-list\n"); return CMD_WARNING; } - vty_show_prefix_entry(vty, json_proto, afi, plist, master, - dtype, seqnum); + vty_show_prefix_entry(vty, json, afi, plist, master, dtype, + seqnum); } else { if (dtype == detail_display || dtype == summary_display) { if (master->recent && !uj) @@ -1107,8 +1103,8 @@ static int vty_show_prefix_list(struct vty *vty, afi_t afi, const char *name, } frr_each (plist, &master->str, plist) - vty_show_prefix_entry(vty, json_proto, afi, plist, - master, dtype, seqnum); + vty_show_prefix_entry(vty, json, afi, plist, master, + dtype, seqnum); } return vty_json(vty, json); @@ -1227,7 +1223,7 @@ static int vty_clear_prefix_list(struct vty *vty, afi_t afi, const char *name, #include "lib/plist_clippy.c" -DEFPY (show_ip_prefix_list, +DEFPY_NOSH (show_ip_prefix_list, show_ip_prefix_list_cmd, "show ip prefix-list [PREFIXLIST4_NAME$name [seq$dseq (1-4294967295)$arg]] [json$uj]", SHOW_STR @@ -1239,6 +1235,7 @@ DEFPY (show_ip_prefix_list, JSON_STR) { enum display_type dtype = normal_display; + if (dseq) dtype = sequential_display; @@ -1258,6 +1255,7 @@ DEFPY (show_ip_prefix_list_prefix, "First matched prefix\n") { enum display_type dtype = normal_display; + if (dl) dtype = longer_display; else if (dfm) @@ -1267,7 +1265,7 @@ DEFPY (show_ip_prefix_list_prefix, dtype); } -DEFPY (show_ip_prefix_list_summary, +DEFPY_NOSH (show_ip_prefix_list_summary, show_ip_prefix_list_summary_cmd, "show ip prefix-list summary [PREFIXLIST4_NAME$name] [json$uj]", SHOW_STR @@ -1281,7 +1279,7 @@ DEFPY (show_ip_prefix_list_summary, summary_display, !!uj); } -DEFPY (show_ip_prefix_list_detail, +DEFPY_NOSH (show_ip_prefix_list_detail, show_ip_prefix_list_detail_cmd, "show ip prefix-list detail [PREFIXLIST4_NAME$name] [json$uj]", SHOW_STR @@ -1307,7 +1305,7 @@ DEFPY (clear_ip_prefix_list, return vty_clear_prefix_list(vty, AFI_IP, name, prefix_str); } -DEFPY (show_ipv6_prefix_list, +DEFPY_NOSH(show_ipv6_prefix_list, show_ipv6_prefix_list_cmd, "show ipv6 prefix-list [PREFIXLIST6_NAME$name [seq$dseq (1-4294967295)$arg]] [json$uj]", SHOW_STR @@ -1319,6 +1317,7 @@ DEFPY (show_ipv6_prefix_list, JSON_STR) { enum display_type dtype = normal_display; + if (dseq) dtype = sequential_display; @@ -1338,6 +1337,7 @@ DEFPY (show_ipv6_prefix_list_prefix, "First matched prefix\n") { enum display_type dtype = normal_display; + if (dl) dtype = longer_display; else if (dfm) @@ -1347,7 +1347,7 @@ DEFPY (show_ipv6_prefix_list_prefix, prefix_str, dtype); } -DEFPY (show_ipv6_prefix_list_summary, +DEFPY_NOSH (show_ipv6_prefix_list_summary, show_ipv6_prefix_list_summary_cmd, "show ipv6 prefix-list summary [PREFIXLIST6_NAME$name] [json$uj]", SHOW_STR @@ -1361,7 +1361,7 @@ DEFPY (show_ipv6_prefix_list_summary, summary_display, !!uj); } -DEFPY (show_ipv6_prefix_list_detail, +DEFPY_NOSH (show_ipv6_prefix_list_detail, show_ipv6_prefix_list_detail_cmd, "show ipv6 prefix-list detail [PREFIXLIST6_NAME$name] [json$uj]", SHOW_STR diff --git a/lib/prefix.c b/lib/prefix.c index f342c4c..2485c3e 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -1124,6 +1124,15 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size) return str; } +void prefix_mcast_ip_dump(const char *onfail, const struct ipaddr *addr, + char *buf, int buf_size) +{ + if (ipaddr_is_zero(addr)) + strlcpy(buf, "*", buf_size); + else + (void)snprintfrr(buf, buf_size, "%pIA", addr); +} + static ssize_t prefixhost2str(struct fbuf *fbuf, union prefixconstptr pu) { const struct prefix *p = pu.p; @@ -1166,7 +1175,7 @@ const char *prefix_sg2str(const struct prefix_sg *sg, char *sg_str) char src_str[INET_ADDRSTRLEN]; char grp_str[INET_ADDRSTRLEN]; - prefix_mcast_inet4_dump("<src?>", sg->src, src_str, sizeof(src_str)); + prefix_mcast_ip_dump("<src?>", &sg->src, src_str, sizeof(src_str)); prefix_mcast_inet4_dump("<grp?>", sg->grp, grp_str, sizeof(grp_str)); snprintf(sg_str, PREFIX_SG_STR_LEN, "(%s,%s)", src_str, grp_str); @@ -1637,10 +1646,10 @@ static ssize_t printfrr_psg(struct fbuf *buf, struct printfrr_eargs *ea, if (!sg) return bputs(buf, "(null)"); - if (sg->src.s_addr == INADDR_ANY) + if (ipaddr_is_zero(&sg->src)) ret += bputs(buf, "(*,"); else - ret += bprintfrr(buf, "(%pI4,", &sg->src); + ret += bprintfrr(buf, "(%pIA,", &sg->src); if (sg->grp.s_addr == INADDR_ANY) ret += bputs(buf, "*)"); diff --git a/lib/prefix.h b/lib/prefix.h index 14f2695..2d679d0 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -52,10 +52,10 @@ typedef enum { /* Maximum number of VTEPs per-ES - * XXX - temporary limit for allocating strings etc. */ -#define ES_VTEP_MAX_CNT 10 -#define ES_VTEP_LIST_STR_SZ (ES_VTEP_MAX_CNT * 16) +#define ES_VTEP_MAX_CNT 10 +#define ES_VTEP_LIST_STR_SZ (ES_VTEP_MAX_CNT * IPADDR_STRING_SIZE) -#define ETHER_ADDR_STRLEN (3*ETH_ALEN) +#define ETHER_ADDR_STRLEN (3 * ETH_ALEN) /* * there isn't a portable ethernet address type. We define our * own to simplify internal handling @@ -282,7 +282,7 @@ struct prefix_fs { struct prefix_sg { uint8_t family; uint16_t prefixlen; - struct in_addr src __attribute__((aligned(8))); + struct ipaddr src __attribute__((aligned(8))); struct in_addr grp; }; @@ -415,6 +415,8 @@ extern int str2prefix(const char *string, struct prefix *prefix); #define PREFIX2STR_BUFFER PREFIX_STRLEN +extern void prefix_mcast_ip_dump(const char *onfail, const struct ipaddr *addr, + char *buf, int buf_size); extern void prefix_mcast_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size); extern const char *prefix_sg2str(const struct prefix_sg *sg, char *str); diff --git a/lib/printf/README b/lib/printf/README index 46d51ce..3c3ef74 100644 --- a/lib/printf/README +++ b/lib/printf/README @@ -1,6 +1,7 @@ This is the printf implementation from FreeBSD. The history of this code is: - imported on 2019-05-12, from SVN revision 347514 - resynced on 2023-09-03, to pick up `%b` implementation +- resynced on 2024-03-10, to pick up `%w[f](8|16|32|64)d` implementation Please don't reindent or otherwise mangle the files to make importing fixes easy (not that there are significant changes likely to happen...) diff --git a/lib/printf/printflocal.h b/lib/printf/printflocal.h index 4b03091..93318c8 100644 --- a/lib/printf/printflocal.h +++ b/lib/printf/printflocal.h @@ -52,6 +52,7 @@ #define PTRDIFFT 0x800 /* ptrdiff_t */ #define INTMAXT 0x1000 /* intmax_t */ #define CHARINT 0x2000 /* print char using int format */ +#define FASTINT 0x4000 /* int_fastN_t */ /* * Macros for converting digits to letters and vice versa diff --git a/lib/printf/vfprintf.c b/lib/printf/vfprintf.c index 2083642..3f6700c 100644 --- a/lib/printf/vfprintf.c +++ b/lib/printf/vfprintf.c @@ -416,6 +416,49 @@ reswitch: switch (ch) { case 't': flags |= PTRDIFFT; goto rflag; + case 'w': + /* + * Fixed-width integer types. On all platforms we + * support, int8_t is equivalent to char, int16_t + * is equivalent to short, int32_t is equivalent + * to int, int64_t is equivalent to long long int. + * Furthermore, int_fast8_t, int_fast16_t and + * int_fast32_t are equivalent to int, and + * int_fast64_t is equivalent to long long int. + */ + flags &= ~(CHARINT|SHORTINT|LONGINT|LLONGINT|INTMAXT); + if (fmt[0] == 'f') { + flags |= FASTINT; + fmt++; + } else { + flags &= ~FASTINT; + } + if (fmt[0] == '8') { + if (!(flags & FASTINT)) + flags |= CHARINT; + else + (void) 0; /* no flag set = 32 */ + fmt += 1; + } else if (fmt[0] == '1' && fmt[1] == '6') { + if (!(flags & FASTINT)) + flags |= SHORTINT; + else + (void) 0; /* no flag set = 32 */ + fmt += 2; + } else if (fmt[0] == '3' && fmt[1] == '2') { + /* no flag set = 32 */ + fmt += 2; + } else if (fmt[0] == '6' && fmt[1] == '4') { + flags |= LLONGINT; + fmt += 2; + } else { + if (flags & FASTINT) { + flags &= ~FASTINT; + fmt--; + } + goto invalid; + } + goto rflag; case 'z': flags |= SIZET; goto rflag; @@ -684,6 +727,7 @@ number: if ((dprec = prec) >= 0) default: /* "%?" prints ?, unless ? is NUL */ if (ch == '\0') goto done; +invalid: /* pretend it was %c with argument ch */ buf[0] = ch; cp = buf; diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index 88b341c..8e2e497 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -490,29 +490,33 @@ DEFPY_YANG( DEFPY_YANG( match_tag, match_tag_cmd, - "match tag (1-4294967295)$tag", + "match tag <untagged$untagged|(1-4294967295)$tagged>", MATCH_STR "Match tag of route\n" + "Untagged route\n" "Tag value\n") { const char *xpath = "./match-condition[condition='frr-route-map:match-tag']"; char xpath_value[XPATH_MAXLEN]; + char value[64]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); snprintf(xpath_value, sizeof(xpath_value), "%s/rmap-match-condition/tag", xpath); - nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, tag_str); + snprintf(value, sizeof(value), "%lu", tagged ? tagged : 0); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, value); return nb_cli_apply_changes(vty, NULL); } DEFPY_YANG( no_match_tag, no_match_tag_cmd, - "no match tag [(1-4294967295)]", + "no match tag [<untagged|(1-4294967295)>]", NO_STR MATCH_STR "Match tag of route\n" + "Untagged route\n" "Tag value\n") { const char *xpath = @@ -581,9 +585,15 @@ void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode, yang_dnode_get_string(dnode, "./rmap-match-condition/metric")); } else if (IS_MATCH_TAG(condition)) { - vty_out(vty, " match tag %s\n", - yang_dnode_get_string(dnode, - "./rmap-match-condition/tag")); + uint32_t tag = + strtoul(yang_dnode_get_string(dnode, + "./rmap-match-condition/tag"), + NULL, 10); + + if (!tag) + vty_out(vty, " match tag untagged\n"); + else + vty_out(vty, " match tag %u\n", tag); } else if (IS_MATCH_IPv4_PREFIX_LEN(condition)) { vty_out(vty, " match ip address prefix-len %s\n", yang_dnode_get_string( @@ -973,28 +983,32 @@ DEFPY_YANG(no_set_max_metric, no_set_max_metric_cmd, DEFPY_YANG( set_tag, set_tag_cmd, - "set tag (1-4294967295)$tag", + "set tag <untagged$untagged|(1-4294967295)$tagged>", SET_STR "Tag value for routing protocol\n" + "Untagged route\n" "Tag value\n") { const char *xpath = "./set-action[action='frr-route-map:set-tag']"; char xpath_value[XPATH_MAXLEN]; + char value[64]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); snprintf(xpath_value, sizeof(xpath_value), "%s/rmap-set-action/tag", xpath); - nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, tag_str); + snprintf(value, sizeof(value), "%lu", tagged ? tagged : 0); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, value); return nb_cli_apply_changes(vty, NULL); } DEFPY_YANG( no_set_tag, no_set_tag_cmd, - "no set tag [(1-4294967295)]", + "no set tag [<untagged|(1-4294967295)>]", NO_STR SET_STR "Tag value for routing protocol\n" + "Untagged route\n" "Tag value\n") { const char *xpath = "./set-action[action='frr-route-map:set-tag']"; @@ -1101,8 +1115,15 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode, yang_dnode_get_string(dnode, "./rmap-set-action/max-metric")); } else if (IS_SET_TAG(action)) { - vty_out(vty, " set tag %s\n", - yang_dnode_get_string(dnode, "rmap-set-action/tag")); + uint32_t tag = + strtoul(yang_dnode_get_string(dnode, + "rmap-set-action/tag"), + NULL, 10); + + if (!tag) + vty_out(vty, " set tag untagged\n"); + else + vty_out(vty, " set tag %u\n", tag); } else if (IS_SET_SR_TE_COLOR(action)) { vty_out(vty, " set sr-te color %s\n", yang_dnode_get_string(dnode, @@ -1252,14 +1273,14 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode, } else if (IS_SET_EXTCOMMUNITY_LB(action)) { enum ecommunity_lb_type lb_type; char str[VTY_BUFSIZ]; - uint16_t bandwidth; + uint32_t bandwidth; lb_type = yang_dnode_get_enum( dnode, "./rmap-set-action/frr-bgp-route-map:extcommunity-lb/lb-type"); switch (lb_type) { case EXPLICIT_BANDWIDTH: - bandwidth = yang_dnode_get_uint16( + bandwidth = yang_dnode_get_uint32( dnode, "./rmap-set-action/frr-bgp-route-map:extcommunity-lb/bandwidth"); snprintf(str, sizeof(str), "%d", bandwidth); diff --git a/lib/routing_nb.h b/lib/routing_nb.h index e805e1c..26b4cf0 100644 --- a/lib/routing_nb.h +++ b/lib/routing_nb.h @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + #ifndef _FRR_ROUTING_NB_H_ #define _FRR_ROUTING_NB_H_ diff --git a/lib/sigevent.c b/lib/sigevent.c index 06f80db..3e69f28 100644 --- a/lib/sigevent.c +++ b/lib/sigevent.c @@ -240,7 +240,17 @@ core_handler(int signo, siginfo_t *siginfo, void *context) /* dump memory stats on core */ log_memstats(stderr, "core_handler"); - zlog_tls_buffer_fini(); + /* + * This is a buffer flush because FRR is going down + * hard. This is especially important if the crash + * was caused by a memory operation and if we call + * zlog_tls_buffer_fini() then it has memory + * operations as well. This will cause the + * core dump to not happen. BAD MOJO + * So this is intentional, let's try to flush + * what we can and let the crash happen. + */ + zlog_tls_buffer_flush(); /* give the kernel a chance to generate a coredump */ sigaddset(&sigset, signo); @@ -99,6 +99,7 @@ struct index_oid { */ extern bool smux_enabled(void); +extern void libagentx_init(void); extern void smux_init(struct event_loop *tm); extern void smux_agentx_enable(void); extern void smux_register_mib(const char *, struct variable *, size_t, int, @@ -94,9 +94,11 @@ const char *seg6local_context2str(char *str, size_t size, snprintf(str, size, "table %u", ctx->table); return str; - case ZEBRA_SEG6_LOCAL_ACTION_END_DX2: case ZEBRA_SEG6_LOCAL_ACTION_END_B6: case ZEBRA_SEG6_LOCAL_ACTION_END_B6_ENCAP: + snprintfrr(str, size, "nh6 %pI6", &ctx->nh6); + return str; + case ZEBRA_SEG6_LOCAL_ACTION_END_DX2: case ZEBRA_SEG6_LOCAL_ACTION_END_BM: case ZEBRA_SEG6_LOCAL_ACTION_END_S: case ZEBRA_SEG6_LOCAL_ACTION_END_AS: diff --git a/lib/stream.c b/lib/stream.c index c6de3ae..bb90f3b 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -921,7 +921,7 @@ int stream_put_in_addr(struct stream *s, const struct in_addr *addr) return sizeof(uint32_t); } -bool stream_put_ipaddr(struct stream *s, struct ipaddr *ip) +bool stream_put_ipaddr(struct stream *s, const struct ipaddr *ip) { stream_putw(s, ip->ipa_type); @@ -1241,9 +1241,7 @@ void stream_fifo_init(struct stream_fifo *fifo) /* Add new stream to fifo. */ void stream_fifo_push(struct stream_fifo *fifo, struct stream *s) { -#if defined DEV_BUILD size_t max, curmax; -#endif if (fifo->tail) fifo->tail->next = s; @@ -1252,15 +1250,11 @@ void stream_fifo_push(struct stream_fifo *fifo, struct stream *s) fifo->tail = s; fifo->tail->next = NULL; -#if !defined DEV_BUILD - atomic_fetch_add_explicit(&fifo->count, 1, memory_order_release); -#else max = atomic_fetch_add_explicit(&fifo->count, 1, memory_order_release); curmax = atomic_load_explicit(&fifo->max_count, memory_order_relaxed); if (max > curmax) atomic_store_explicit(&fifo->max_count, max, memory_order_relaxed); -#endif } void stream_fifo_push_safe(struct stream_fifo *fifo, struct stream *s) diff --git a/lib/stream.h b/lib/stream.h index 85eebb4..e48cedc 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -105,9 +105,7 @@ struct stream_fifo { /* number of streams in this fifo */ atomic_size_t count; -#if defined DEV_BUILD atomic_size_t max_count; -#endif struct stream *head; struct stream *tail; @@ -177,7 +175,7 @@ extern int stream_putq(struct stream *, uint64_t); extern int stream_putq_at(struct stream *, size_t, uint64_t); extern int stream_put_ipv4(struct stream *, uint32_t); extern int stream_put_in_addr(struct stream *s, const struct in_addr *addr); -extern bool stream_put_ipaddr(struct stream *s, struct ipaddr *ip); +extern bool stream_put_ipaddr(struct stream *s, const struct ipaddr *ip); extern int stream_put_in_addr_at(struct stream *s, size_t putp, const struct in_addr *addr); extern int stream_put_in6_addr_at(struct stream *s, size_t putp, diff --git a/lib/subdir.am b/lib/subdir.am index 6893049..3264f31 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -53,9 +53,12 @@ lib_libfrr_la_SOURCES = \ lib/jhash.c \ lib/json.c \ lib/keychain.c \ + lib/keychain_cli.c \ + lib/keychain_nb.c \ lib/ldp_sync.c \ lib/lib_errors.c \ lib/lib_vty.c \ + lib/libagentx.c \ lib/libfrr.c \ lib/libfrr_trace.c \ lib/linklist.c \ @@ -129,6 +132,7 @@ lib_libfrr_la_SOURCES = \ lib/zlog_5424.c \ lib/zlog_5424_cli.c \ lib/zlog_live.c \ + lib/zlog_recirculate.c \ lib/zlog_targets.c \ lib/printf/printf-pos.c \ lib/printf/vfprintf.c \ @@ -148,7 +152,10 @@ nodist_lib_libfrr_la_SOURCES = \ yang/frr-vrf.yang.c \ yang/frr-routing.yang.c \ yang/frr-nexthop.yang.c \ + yang/ietf/frr-deviations-ietf-key-chain.yang.c \ yang/ietf/ietf-routing-types.yang.c \ + yang/ietf/ietf-netconf-acm.yang.c \ + yang/ietf/ietf-key-chain.yang.c \ yang/ietf/ietf-interfaces.yang.c \ yang/ietf/ietf-bgp-types.yang.c \ yang/frr-module-translator.yang.c \ @@ -181,6 +188,7 @@ clippy_scan += \ lib/if.c \ lib/filter_cli.c \ lib/if_rmap.c \ + lib/keychain_cli.c \ lib/log_vty.c \ lib/mgmt_be_client.c \ lib/mgmt_fe_client.c \ @@ -245,6 +253,7 @@ pkginclude_HEADERS += \ lib/ldp_sync.h \ lib/lib_errors.h \ lib/lib_vty.h \ + lib/libagentx.h \ lib/libfrr.h \ lib/libfrr_trace.h \ lib/libospf.h \ @@ -324,6 +333,7 @@ pkginclude_HEADERS += \ lib/zlog.h \ lib/zlog_5424.h \ lib/zlog_live.h \ + lib/zlog_recirculate.h \ lib/zlog_targets.h \ lib/pbr.h \ lib/tc.h \ @@ -403,18 +413,6 @@ lib_libfrrzmq_la_SOURCES = \ #end # -# Tail-f's ConfD support -# -if CONFD -module_LTLIBRARIES += lib/confd.la -endif - -lib_confd_la_CFLAGS = $(AM_CFLAGS) $(CONFD_CFLAGS) -lib_confd_la_LDFLAGS = $(MODULE_LDFLAGS) -lib_confd_la_LIBADD = lib/libfrr.la $(CONFD_LIBS) -lib_confd_la_SOURCES = lib/northbound_confd.c - -# # Sysrepo support # if SYSREPO diff --git a/lib/typerb.h b/lib/typerb.h index b020a66..93370e1 100644 --- a/lib/typerb.h +++ b/lib/typerb.h @@ -9,8 +9,10 @@ #ifndef _FRR_TYPERB_H #define _FRR_TYPERB_H +#ifndef _TYPESAFE_EXPAND_MACROS #include <string.h> #include "typesafe.h" +#endif /* _TYPESAFE_EXPAND_MACROS */ #ifdef __cplusplus extern "C" { diff --git a/lib/typesafe.h b/lib/typesafe.h index 93258c5..fc02804 100644 --- a/lib/typesafe.h +++ b/lib/typesafe.h @@ -6,10 +6,12 @@ #ifndef _FRR_TYPESAFE_H #define _FRR_TYPESAFE_H +#ifndef _TYPESAFE_EXPAND_MACROS #include <stddef.h> #include <stdint.h> #include <stdbool.h> #include "compiler.h" +#endif /* _TYPESAFE_EXPAND_MACROS */ #ifdef __cplusplus extern "C" { @@ -39,6 +39,7 @@ #include "libfrr.h" #include "frrstr.h" #include "lib_errors.h" +#include <libyang/version.h> #include "northbound_cli.h" #include "printfrr.h" #include "json.h" @@ -389,6 +390,21 @@ int vty_json_no_pretty(struct vty *vty, struct json_object *json) return vty_json_helper(vty, json, JSON_C_TO_STRING_NOSLASHESCAPE); } + +void vty_json_key(struct vty *vty, const char *key, bool *first_key) +{ + vty_out(vty, "%s\"%s\":", *first_key ? "{" : ",", key); + *first_key = false; +} + +void vty_json_close(struct vty *vty, bool first_key) +{ + if (first_key) + /* JSON was not opened */ + vty_out(vty, "{"); + vty_out(vty, "}\n"); +} + void vty_json_empty(struct vty *vty, struct json_object *json) { json_object *jsonobj = json; @@ -3486,7 +3502,7 @@ static void vty_mgmt_server_connected(struct mgmt_fe_client *client, /* Start or stop listening for vty connections */ if (connected) - frr_vty_serv_start(); + frr_vty_serv_start(true); else frr_vty_serv_stop(); } @@ -3570,12 +3586,14 @@ static void vty_mgmt_set_config_result_notified( zlog_err("SET_CONFIG request for client 0x%" PRIx64 " failed, Error: '%s'", client_id, errmsg_if_any ? errmsg_if_any : "Unknown"); - vty_out(vty, "ERROR: SET_CONFIG request failed, Error: %s\n", - errmsg_if_any ? errmsg_if_any : "Unknown"); + vty_out(vty, "%% Configuration failed.\n\n"); + if (errmsg_if_any) + vty_out(vty, "%s\n", errmsg_if_any); } else { debug_fe_client("SET_CONFIG request for client 0x%" PRIx64 - " req-id %" PRIu64 " was successfull", - client_id, req_id); + " req-id %" PRIu64 " was successfull%s%s", + client_id, req_id, errmsg_if_any ? ": " : "", + errmsg_if_any ?: ""); } if (implicit_commit) { @@ -3602,12 +3620,14 @@ static void vty_mgmt_commit_config_result_notified( zlog_err("COMMIT_CONFIG request for client 0x%" PRIx64 " failed, Error: '%s'", client_id, errmsg_if_any ? errmsg_if_any : "Unknown"); - vty_out(vty, "ERROR: COMMIT_CONFIG request failed, Error: %s\n", - errmsg_if_any ? errmsg_if_any : "Unknown"); + vty_out(vty, "%% Configuration failed.\n\n"); + if (errmsg_if_any) + vty_out(vty, "%s\n", errmsg_if_any); } else { debug_fe_client("COMMIT_CONFIG request for client 0x%" PRIx64 - " req-id %" PRIu64 " was successfull", - client_id, req_id); + " req-id %" PRIu64 " was successfull%s%s", + client_id, req_id, errmsg_if_any ? ": " : "", + errmsg_if_any ?: ""); if (errmsg_if_any) vty_out(vty, "MGMTD: %s\n", errmsg_if_any); } @@ -3638,8 +3658,9 @@ static int vty_mgmt_get_data_result_notified( } debug_fe_client("GET_DATA request succeeded, client 0x%" PRIx64 - " req-id %" PRIu64, - client_id, req_id); + " req-id %" PRIu64 "%s%s", + client_id, req_id, errmsg_if_any ? ": " : "", + errmsg_if_any ?: ""); if (req_id != mgmt_last_req_id) { mgmt_last_req_id = req_id; @@ -3668,15 +3689,24 @@ static ssize_t vty_mgmt_libyang_print(void *user_data, const void *buf, } static void vty_out_yang_error(struct vty *vty, LYD_FORMAT format, - struct ly_err_item *ei) + const struct ly_err_item *ei) { +#if (LY_VERSION_MAJOR < 3) +#define data_path path +#else +#define data_path data_path +#endif bool have_apptag = ei->apptag && ei->apptag[0] != 0; - bool have_path = ei->path && ei->path[0] != 0; + bool have_path = ei->data_path && ei->data_path[0] != 0; bool have_msg = ei->msg && ei->msg[0] != 0; const char *severity = NULL; const char *evalid = NULL; const char *ecode = NULL; +#if (LY_VERSION_MAJOR < 3) LY_ERR err = ei->no; +#else + LY_ERR err = ei->err; +#endif if (ei->level == LY_LLERR) severity = "error"; @@ -3701,7 +3731,8 @@ static void vty_out_yang_error(struct vty *vty, LYD_FORMAT format, vty_out(vty, "<error-validation>%s</error-validation>\n", evalid); if (have_path) - vty_out(vty, "<error-path>%s</error-path>\n", ei->path); + vty_out(vty, "<error-path>%s</error-path>\n", + ei->data_path); if (have_apptag) vty_out(vty, "<error-app-tag>%s</error-app-tag>\n", ei->apptag); @@ -3720,7 +3751,7 @@ static void vty_out_yang_error(struct vty *vty, LYD_FORMAT format, if (evalid) vty_out(vty, ", \"error-validation\": \"%s\"", evalid); if (have_path) - vty_out(vty, ", \"error-path\": \"%s\"", ei->path); + vty_out(vty, ", \"error-path\": \"%s\"", ei->data_path); if (have_apptag) vty_out(vty, ", \"error-app-tag\": \"%s\"", ei->apptag); if (have_msg) @@ -3737,18 +3768,19 @@ static void vty_out_yang_error(struct vty *vty, LYD_FORMAT format, if (evalid) vty_out(vty, " invalid: %s", evalid); if (have_path) - vty_out(vty, " path: %s", ei->path); + vty_out(vty, " path: %s", ei->data_path); if (have_apptag) vty_out(vty, " app-tag: %s", ei->apptag); if (have_msg) vty_out(vty, " msg: %s", ei->msg); break; } +#undef data_path } static uint vty_out_yang_errors(struct vty *vty, LYD_FORMAT format) { - struct ly_err_item *ei = ly_err_first(ly_native_ctx); + const struct ly_err_item *ei = ly_err_first(ly_native_ctx); uint count; if (!ei) @@ -3824,6 +3856,43 @@ static int vty_mgmt_get_tree_result_notified( return 0; } +static int vty_mgmt_edit_result_notified(struct mgmt_fe_client *client, + uintptr_t user_data, + uint64_t client_id, uint64_t session_id, + uintptr_t session_ctx, uint64_t req_id, + const char *xpath) +{ + struct vty *vty = (struct vty *)session_ctx; + + debug_fe_client("EDIT request for client 0x%" PRIx64 " req-id %" PRIu64 + " was successful, xpath: %s", + client_id, req_id, xpath); + + vty_mgmt_resume_response(vty, CMD_SUCCESS); + + return 0; +} + +static int vty_mgmt_rpc_result_notified(struct mgmt_fe_client *client, + uintptr_t user_data, uint64_t client_id, + uint64_t session_id, + uintptr_t session_ctx, uint64_t req_id, + const char *result) +{ + struct vty *vty = (struct vty *)session_ctx; + + debug_fe_client("RPC request for client 0x%" PRIx64 " req-id %" PRIu64 + " was successful", + client_id, req_id); + + if (result) + vty_out(vty, "%s\n", result); + + vty_mgmt_resume_response(vty, CMD_SUCCESS); + + return 0; +} + static int vty_mgmt_error_notified(struct mgmt_fe_client *client, uintptr_t user_data, uint64_t client_id, uint64_t session_id, uintptr_t session_ctx, @@ -3865,6 +3934,8 @@ static struct mgmt_fe_client_cbs mgmt_cbs = { .commit_config_notify = vty_mgmt_commit_config_result_notified, .get_data_notify = vty_mgmt_get_data_result_notified, .get_tree_notify = vty_mgmt_get_tree_result_notified, + .edit_notify = vty_mgmt_edit_result_notified, + .rpc_notify = vty_mgmt_rpc_result_notified, .error_notify = vty_mgmt_error_notified, }; @@ -4120,6 +4191,47 @@ int vty_mgmt_send_get_data_req(struct vty *vty, uint8_t datastore, return 0; } +int vty_mgmt_send_edit_req(struct vty *vty, uint8_t datastore, + LYD_FORMAT request_type, uint8_t flags, + uint8_t operation, const char *xpath, + const char *data) +{ + vty->mgmt_req_id++; + + if (mgmt_fe_send_edit_req(mgmt_fe_client, vty->mgmt_session_id, + vty->mgmt_req_id, datastore, request_type, + flags, operation, xpath, data)) { + zlog_err("Failed to send EDIT to MGMTD session-id: %" PRIu64 + " req-id %" PRIu64 ".", + vty->mgmt_session_id, vty->mgmt_req_id); + vty_out(vty, "Failed to send EDIT to MGMTD!\n"); + return -1; + } + + vty->mgmt_req_pending_cmd = "MESSAGE_EDIT_REQ"; + + return 0; +} + +int vty_mgmt_send_rpc_req(struct vty *vty, LYD_FORMAT request_type, + const char *xpath, const char *data) +{ + vty->mgmt_req_id++; + + if (mgmt_fe_send_rpc_req(mgmt_fe_client, vty->mgmt_session_id, + vty->mgmt_req_id, request_type, xpath, data)) { + zlog_err("Failed to send RPC to MGMTD session-id: %" PRIu64 + " req-id %" PRIu64 ".", + vty->mgmt_session_id, vty->mgmt_req_id); + vty_out(vty, "Failed to send RPC to MGMTD!\n"); + return -1; + } + + vty->mgmt_req_pending_cmd = "MESSAGE_RPC_REQ"; + + return 0; +} + /* Install vty's own commands like `who' command. */ void vty_init(struct event_loop *master_thread, bool do_command_logging) { @@ -122,6 +122,10 @@ struct vty { size_t num_cfg_changes; struct nb_cfg_change cfg_changes[VTY_MAXCFGCHANGES]; + /* Input parameters */ + size_t num_rpc_params; + struct nb_cfg_change rpc_params[VTY_MAXCFGCHANGES]; + /* XPath of the current node */ int xpath_index; char xpath[VTY_MAXDEPTH][XPATH_MAXLEN]; @@ -374,6 +378,8 @@ extern bool vty_set_include(struct vty *vty, const char *regexp); */ extern int vty_json(struct vty *vty, struct json_object *json); extern int vty_json_no_pretty(struct vty *vty, struct json_object *json); +void vty_json_key(struct vty *vty, const char *key, bool *first_key); +void vty_json_close(struct vty *vty, bool first_key); extern void vty_json_empty(struct vty *vty, struct json_object *json); /* post fd to be passed to the vtysh client * fd is owned by the VTY code after this and will be closed when done @@ -419,6 +425,12 @@ extern int vty_mgmt_send_get_req(struct vty *vty, bool is_config, extern int vty_mgmt_send_get_data_req(struct vty *vty, uint8_t datastore, LYD_FORMAT result_type, uint8_t flags, uint8_t defaults, const char *xpath); +extern int vty_mgmt_send_edit_req(struct vty *vty, uint8_t datastore, + LYD_FORMAT request_type, uint8_t flags, + uint8_t operation, const char *xpath, + const char *data); +extern int vty_mgmt_send_rpc_req(struct vty *vty, LYD_FORMAT request_type, + const char *xpath, const char *data); extern int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id, bool lock, bool scok); extern void vty_mgmt_resume_response(struct vty *vty, int ret); @@ -11,13 +11,26 @@ #include "lib_errors.h" #include "yang.h" #include "yang_translator.h" +#include <libyang/version.h> #include "northbound.h" +#include "frrstr.h" #include "lib/config_paths.h" DEFINE_MTYPE_STATIC(LIB, YANG_MODULE, "YANG module"); DEFINE_MTYPE_STATIC(LIB, YANG_DATA, "YANG data structure"); +/* Safe to remove after libyang 2.2.8 */ +#if (LY_VERSION_MAJOR < 3) +#define yang_lyd_find_xpath3(ctx_node, tree, xpath, format, prefix_data, vars, \ + set) \ + lyd_find_xpath3(ctx_node, tree, xpath, vars, set) +#else +#define yang_lyd_find_xpath3(ctx_node, tree, xpath, format, prefix_data, vars, \ + set) \ + lyd_find_xpath3(ctx_node, tree, xpath, LY_VALUE_JSON, NULL, vars, set) +#endif + /* libyang container. */ struct ly_ctx *ly_native_ctx; @@ -199,6 +212,16 @@ next: if (ret == YANG_ITER_STOP) return ret; } + LY_LIST_FOR ((const struct lysc_node *)lysc_node_notifs(snode), child) { + ret = yang_snodes_iterate_subtree(child, module, cb, flags, arg); + if (ret == YANG_ITER_STOP) + return ret; + } + LY_LIST_FOR ((const struct lysc_node *)lysc_node_actions(snode), child) { + ret = yang_snodes_iterate_subtree(child, module, cb, flags, arg); + if (ret == YANG_ITER_STOP) + return ret; + } return ret; } @@ -642,6 +665,16 @@ void yang_dnode_free(struct lyd_node *dnode) lyd_free_all(dnode); } +void yang_dnode_rpc_output_add(struct lyd_node *output, const char *xpath, + const char *value) +{ + LY_ERR err; + + err = lyd_new_path(output, ly_native_ctx, xpath, value, + LYD_NEW_PATH_OUTPUT | LYD_NEW_PATH_UPDATE, NULL); + assert(err == LY_SUCCESS); +} + struct yang_data *yang_data_new(const char *xpath, const char *value) { struct yang_data *data; @@ -691,7 +724,12 @@ struct yang_data *yang_data_list_find(const struct list *list, } /* Make libyang log its errors using FRR logging infrastructure. */ -static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path) +static void ly_zlog_cb(LY_LOG_LEVEL level, const char *msg, const char *data_path +#if !(LY_VERSION_MAJOR < 3) + , + const char *schema_path, uint64_t line +#endif +) { int priority = LOG_ERR; @@ -708,8 +746,14 @@ static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path) break; } - if (path) - zlog(priority, "libyang: %s (%s)", msg, path); + if (data_path) + zlog(priority, "libyang: %s (%s)", msg, data_path); +#if !(LY_VERSION_MAJOR < 3) + else if (schema_path) + zlog(priority, "libyang %s (%s)\n", msg, schema_path); + else if (line) + zlog(priority, "libyang %s (line %" PRIu64 ")\n", msg, line); +#endif else zlog(priority, "libyang: %s", msg); } @@ -736,7 +780,8 @@ LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format, return err; } - err = lyd_find_xpath3(NULL, tree, xpath, NULL, &set); + err = yang_lyd_find_xpath3(NULL, tree, xpath, LY_VALUE_JSON, NULL, NULL, + &set); if (err) { zlog_err("Failed to parse notification: %s", ly_last_errmsg()); lyd_free_all(tree); @@ -753,6 +798,63 @@ LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format, return LY_SUCCESS; } +LY_ERR yang_parse_rpc(const char *xpath, LYD_FORMAT format, const char *data, + bool reply, struct lyd_node **rpc) +{ + const struct lysc_node *snode; + struct lyd_node *parent = NULL; + struct ly_in *in = NULL; + LY_ERR err; + + snode = lys_find_path(ly_native_ctx, NULL, xpath, 0); + if (!snode) { + zlog_err("Failed to find RPC/action schema node: %s", xpath); + return LY_ENOTFOUND; + } + + /* If it's an action, create its parent */ + if (snode->nodetype == LYS_ACTION) { + char *parent_xpath = XSTRDUP(MTYPE_TMP, xpath); + + if (yang_xpath_pop_node(parent_xpath) != NB_OK) { + XFREE(MTYPE_TMP, parent_xpath); + zlog_err("Invalid action xpath: %s", xpath); + return LY_EINVAL; + } + + err = lyd_new_path2(NULL, ly_native_ctx, parent_xpath, NULL, 0, + 0, 0, NULL, &parent); + XFREE(MTYPE_TMP, parent_xpath); + if (err) { + zlog_err("Failed to create parent node for action: %s", + ly_last_errmsg()); + return err; + } + } else if (snode->nodetype != LYS_RPC) { + zlog_err("Schema node is not an RPC/action: %s", xpath); + return LY_EINVAL; + } + + err = ly_in_new_memory(data, &in); + if (err) { + lyd_free_all(parent); + zlog_err("Failed to initialize ly_in: %s", ly_last_errmsg()); + return err; + } + + err = lyd_parse_op(ly_native_ctx, parent, in, format, + reply ? LYD_TYPE_REPLY_YANG : LYD_TYPE_RPC_YANG, + NULL, rpc); + ly_in_free(in, 0); + if (err) { + lyd_free_all(parent); + zlog_err("Failed to parse RPC/action: %s", ly_last_errmsg()); + return err; + } + + return LY_SUCCESS; +} + static ssize_t yang_print_darr(void *arg, const void *buf, size_t count) { uint8_t *dst = darr_append_n(*(uint8_t **)arg, count); @@ -795,7 +897,7 @@ char *yang_convert_lyd_format(const char *data, size_t data_len, assert(out_format != LYD_LYB); - if (in_format != LYD_LYB && !MGMT_MSG_VALIDATE_NUL_TERM(data, data_len)) { + if (in_format != LYD_LYB && (!data_len || data[data_len - 1] != 0)) { zlog_err("Corrupt input data, no NUL terminating byte"); return NULL; } @@ -829,23 +931,29 @@ char *yang_convert_lyd_format(const char *data, size_t data_len, const char *yang_print_errors(struct ly_ctx *ly_ctx, char *buf, size_t buf_len) { - struct ly_err_item *ei; + const struct ly_err_item *ei; ei = ly_err_first(ly_ctx); if (!ei) return ""; strlcpy(buf, "YANG error(s):\n", buf_len); +#if (LY_VERSION_MAJOR < 3) +#define data_path path +#else +#define data_path data_path +#endif for (; ei; ei = ei->next) { - if (ei->path) { + if (ei->data_path) { strlcat(buf, " Path: ", buf_len); - strlcat(buf, ei->path, buf_len); + strlcat(buf, ei->data_path, buf_len); strlcat(buf, "\n", buf_len); } strlcat(buf, " Error: ", buf_len); strlcat(buf, ei->msg, buf_len); strlcat(buf, "\n", buf_len); } +#undef data_path ly_err_clean(ly_ctx, NULL); @@ -897,7 +1005,12 @@ struct ly_ctx *yang_ctx_new_setup(bool embedded_modules, bool explicit_compile) void yang_init(bool embedded_modules, bool defer_compile) { /* Initialize libyang global parameters that affect all containers. */ - ly_set_log_clb(ly_log_cb, 1); + ly_set_log_clb(ly_zlog_cb +#if (LY_VERSION_MAJOR < 3) + , + 1 +#endif + ); ly_log_options(LY_LOLOG | LY_LOSTORE); /* Initialize libyang container for native models. */ @@ -1112,6 +1225,32 @@ int yang_get_node_keys(struct lyd_node *node, struct yang_list_keys *keys) return NB_OK; } +int yang_xpath_pop_node(char *xpath) +{ + int len = strlen(xpath); + bool abs = xpath[0] == '/'; + char *slash; + + /* "//" or "/" => NULL */ + if (abs && (len == 1 || (len == 2 && xpath[1] == '/'))) + return NB_ERR_NOT_FOUND; + + slash = (char *)frrstr_back_to_char(xpath, '/'); + /* "/foo/bar/" or "/foo/bar//" => "/foo " */ + if (slash && slash == &xpath[len - 1]) { + xpath[--len] = 0; + slash = (char *)frrstr_back_to_char(xpath, '/'); + if (slash && slash == &xpath[len - 1]) { + xpath[--len] = 0; + slash = (char *)frrstr_back_to_char(xpath, '/'); + } + } + if (!slash) + return NB_ERR_NOT_FOUND; + *slash = 0; + return NB_OK; +} + /* * ------------------------ * Libyang Future Functions @@ -1209,7 +1348,8 @@ LY_ERR yang_lyd_trim_xpath(struct lyd_node **root, const char *xpath) *root = lyd_first_sibling(*root); - err = lyd_find_xpath3(NULL, *root, xpath, NULL, &set); + err = yang_lyd_find_xpath3(NULL, *root, xpath, LY_VALUE_JSON, NULL, + NULL, &set); if (err) { flog_err_sys(EC_LIB_LIBYANG, "cannot obtain specific result for xpath \"%s\": %s", @@ -1265,6 +1405,42 @@ LY_ERR yang_lyd_trim_xpath(struct lyd_node **root, const char *xpath) #endif } +/* Can be replaced by `lyd_parse_data` with libyang >= 2.1.156 */ +LY_ERR yang_lyd_parse_data(const struct ly_ctx *ctx, struct lyd_node *parent, + struct ly_in *in, LYD_FORMAT format, + uint32_t parse_options, uint32_t validate_options, + struct lyd_node **tree) +{ + struct lyd_node *child; + LY_ERR err; + + err = lyd_parse_data(ctx, parent, in, format, parse_options, + validate_options, tree); + if (err) + return err; + + if (!parent || !(parse_options & LYD_PARSE_ONLY)) + return LY_SUCCESS; + + /* + * Versions prior to 2.1.156 don't return `tree` if `parent` is not NULL + * and validation is disabled (`LYD_PARSE_ONLY`). To work around this, + * go through the children and find the one with `LYD_NEW` flag set. + */ + *tree = NULL; + + LY_LIST_FOR (lyd_child_no_keys(parent), child) { + if (child->flags & LYD_NEW) { + *tree = child; + break; + } + } + + assert(tree); + + return LY_SUCCESS; +} + /* * Safe to remove after libyang v2.1.128 is required */ @@ -45,9 +45,6 @@ struct yang_module { RB_ENTRY(yang_module) entry; const char *name; const struct lys_module *info; -#ifdef HAVE_CONFD - int confd_hash; -#endif #ifdef HAVE_SYSREPO sr_subscription_ctx_t *sr_subscription; struct event *sr_thread; @@ -539,6 +536,21 @@ extern struct lyd_node *yang_dnode_dup(const struct lyd_node *dnode); extern void yang_dnode_free(struct lyd_node *dnode); /* + * Add a libyang data node to an RPC/action output container. + * + * output + * RPC/action output container. + * + * xpath + * XPath of the data node to add, relative to the output container. + * + * value + * String representing the value of the data node. + */ +extern void yang_dnode_rpc_output_add(struct lyd_node *output, + const char *xpath, const char *value); + +/* * Create a new yang_data structure. * * xpath @@ -623,6 +635,25 @@ extern LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format, const char *data, struct lyd_node **notif); /* + * Parse a YANG RPC. + * + * Args: + * xpath: xpath of an RPC/action. + * format: LYD_FORMAT of input data. + * data: input data. + * reply: true if the data represents a reply to an RPC/action. + * rpc: pointer to the libyang data tree to store the parsed RPC/action. + * If data represents an action, the pointer to the action node is + * still returned, but it's part of the full data tree with all its + * parents. + * + * Returns: + * LY_ERR from underlying calls. + */ +LY_ERR yang_parse_rpc(const char *xpath, LYD_FORMAT format, const char *data, + bool reply, struct lyd_node **rpc); + +/* * "Print" the yang tree in `root` into dynamic sized array. * * Args: @@ -771,6 +802,14 @@ extern int yang_get_key_preds(char *s, const struct lysc_node *snode, extern int yang_get_node_keys(struct lyd_node *node, struct yang_list_keys *keys); /** + * yang_xpath_pop_node() - remove the last node from xpath string + * @xpath: an xpath string + * + * Return: NB_OK or NB_ERR_NOT_FOUND if nothing left to pop. + */ +extern int yang_xpath_pop_node(char *xpath); + +/** * yang_resolve_snodes() - Resolve an XPath to matching schema nodes. * @ly_ctx: libyang context to operate on. * @xpath: the path or XPath to resolve. @@ -800,6 +839,11 @@ extern LY_ERR yang_lyd_new_list(struct lyd_node_inner *parent, const struct yang_list_keys *keys, struct lyd_node **nodes); extern LY_ERR yang_lyd_trim_xpath(struct lyd_node **rootp, const char *xpath); +extern LY_ERR yang_lyd_parse_data(const struct ly_ctx *ctx, + struct lyd_node *parent, struct ly_in *in, + LYD_FORMAT format, uint32_t parse_options, + uint32_t validate_options, + struct lyd_node **tree); #ifdef __cplusplus } diff --git a/lib/yang_wrappers.c b/lib/yang_wrappers.c index a013395..4e49a12 100644 --- a/lib/yang_wrappers.c +++ b/lib/yang_wrappers.c @@ -1026,30 +1026,50 @@ void yang_dnode_get_mac(struct ethaddr *mac, const struct lyd_node *dnode, (void)prefix_str2mac(canon, mac); } -struct yang_data *yang_data_new_date_and_time(const char *xpath, time_t time) +struct yang_data *yang_data_new_date_and_time(const char *xpath, time_t time, bool is_monotime) { - struct tm tm; - char timebuf[MONOTIME_STRLEN]; - struct timeval _time, time_real; - char *ts_dot; - uint16_t buflen; + struct yang_data *yd; + char *times = NULL; - _time.tv_sec = time; - _time.tv_usec = 0; - monotime_to_realtime(&_time, &time_real); + if (is_monotime) { + struct timeval _time = { time, 0 }; + struct timeval time_real; - gmtime_r(&time_real.tv_sec, &tm); + monotime_to_realtime(&_time, &time_real); + time = time_real.tv_sec; + } + + (void)ly_time_time2str(time, NULL, ×); + yd = yang_data_new(xpath, times); + free(times); + + return yd; +} + +struct timespec yang_dnode_get_date_and_timespec(const struct lyd_node *dnode, + const char *xpath_fmt, ...) +{ + const char *canon = YANG_DNODE_XPATH_GET_CANON(dnode, xpath_fmt); + struct timespec ts; + LY_ERR err; + + err = ly_time_str2ts(canon, &ts); + assert(!err); + + return ts; +} - /* rfc-3339 format */ - strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%S", &tm); - buflen = strlen(timebuf); - ts_dot = timebuf + buflen; +time_t yang_dnode_get_date_and_time(const struct lyd_node *dnode, + const char *xpath_fmt, ...) +{ + const char *canon = YANG_DNODE_XPATH_GET_CANON(dnode, xpath_fmt); + time_t time; + LY_ERR err; - /* microseconds and appends Z */ - snprintfrr(ts_dot, sizeof(timebuf) - buflen, ".%06luZ", - (unsigned long)time_real.tv_usec); + err = ly_time_str2time(canon, &time, NULL); + assert(!err); - return yang_data_new(xpath, timebuf); + return time; } float yang_dnode_get_bandwidth_ieee_float32(const struct lyd_node *dnode, diff --git a/lib/yang_wrappers.h b/lib/yang_wrappers.h index 59b5b13..d3d8419 100644 --- a/lib/yang_wrappers.h +++ b/lib/yang_wrappers.h @@ -7,6 +7,7 @@ #ifndef _FRR_NORTHBOUND_WRAPPERS_H_ #define _FRR_NORTHBOUND_WRAPPERS_H_ +#include <libyang/libyang.h> #include "prefix.h" #ifdef __cplusplus @@ -200,7 +201,14 @@ extern void yang_dnode_get_mac(struct ethaddr *mac, const struct lyd_node *dnode /*data-and-time */ extern struct yang_data *yang_data_new_date_and_time(const char *xpath, - time_t time); + time_t time, + bool is_monotime); +struct timespec yang_dnode_get_date_and_timespec(const struct lyd_node *dnode, + const char *xpath_fmt, ...) + PRINTFRR(2, 3); +time_t yang_dnode_get_date_and_time(const struct lyd_node *dnode, + const char *xpath_fmt, ...) + PRINTFRR(2, 3); /* rt-types:bandwidth-ieee-float32 */ extern float yang_dnode_get_bandwidth_ieee_float32(const struct lyd_node *dnode, diff --git a/lib/zclient.c b/lib/zclient.c index 51ebb56..1aab7b4 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -282,6 +282,7 @@ static void zclient_flush_data(struct event *thread) zclient->sock, &zclient->t_write); break; case BUFFER_EMPTY: + /* Currently only Sharpd and Bgpd has callbacks defined */ if (zclient->zebra_buffer_write_ready) (*zclient->zebra_buffer_write_ready)(); break; @@ -1039,7 +1040,7 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, } if (api_nh->weight) - stream_putl(s, api_nh->weight); + stream_putq(s, api_nh->weight); /* Router MAC for EVPN routes. */ if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_EVPN)) @@ -1124,6 +1125,7 @@ int zapi_srv6_locator_encode(struct stream *s, const struct srv6_locator *l) stream_put(s, l->name, strlen(l->name)); stream_putw(s, l->prefix.prefixlen); stream_put(s, &l->prefix.prefix, sizeof(l->prefix.prefix)); + stream_putc(s, l->flags); return 0; } @@ -1139,6 +1141,7 @@ int zapi_srv6_locator_decode(struct stream *s, struct srv6_locator *l) STREAM_GETW(s, l->prefix.prefixlen); STREAM_GET(&l->prefix.prefix, s, sizeof(l->prefix.prefix)); l->prefix.family = AF_INET6; + STREAM_GETC(s, l->flags); return 0; stream_failure: @@ -1411,7 +1414,7 @@ int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh, } if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_WEIGHT)) - STREAM_GETL(s, api_nh->weight); + STREAM_GETQ(s, api_nh->weight); /* Router MAC for EVPN routes. */ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_EVPN)) @@ -2171,6 +2174,7 @@ int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh, znh->weight = nh->weight; znh->ifindex = nh->ifindex; znh->gate = nh->gate; + znh->srte_color = nh->srte_color; if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ONLINK)) SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); @@ -4669,21 +4673,25 @@ char *zclient_dump_route_flags(uint32_t flags, char *buf, size_t len) return buf; } - snprintfrr( - buf, len, "%s%s%s%s%s%s%s%s%s%s", - CHECK_FLAG(flags, ZEBRA_FLAG_ALLOW_RECURSION) ? "Recursion " - : "", - CHECK_FLAG(flags, ZEBRA_FLAG_SELFROUTE) ? "Self " : "", - CHECK_FLAG(flags, ZEBRA_FLAG_IBGP) ? "iBGP " : "", - CHECK_FLAG(flags, ZEBRA_FLAG_SELECTED) ? "Selected " : "", - CHECK_FLAG(flags, ZEBRA_FLAG_FIB_OVERRIDE) ? "Override " : "", - CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE) ? "Evpn " : "", - CHECK_FLAG(flags, ZEBRA_FLAG_RR_USE_DISTANCE) ? "RR Distance " - : "", - CHECK_FLAG(flags, ZEBRA_FLAG_TRAPPED) ? "Trapped " : "", - CHECK_FLAG(flags, ZEBRA_FLAG_OFFLOADED) ? "Offloaded " : "", - CHECK_FLAG(flags, ZEBRA_FLAG_OFFLOAD_FAILED) ? "Offload Failed " - : ""); + snprintfrr(buf, len, "%s%s%s%s%s%s%s%s%s%s%s", + CHECK_FLAG(flags, ZEBRA_FLAG_ALLOW_RECURSION) ? "Recursion " + : "", + + CHECK_FLAG(flags, ZEBRA_FLAG_SELFROUTE) ? "Self " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_IBGP) ? "iBGP " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_SELECTED) ? "Selected " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_FIB_OVERRIDE) ? "Override " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE) ? "Evpn " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_RR_USE_DISTANCE) ? "RR Distance " + : "", + + CHECK_FLAG(flags, ZEBRA_FLAG_TRAPPED) ? "Trapped " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_OFFLOADED) ? "Offloaded " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_OFFLOAD_FAILED) + ? "Offload Failed " + : "", + CHECK_FLAG(flags, ZEBRA_FLAG_OUTOFSYNC) ? "OutOfSync " : ""); + return buf; } diff --git a/lib/zclient.h b/lib/zclient.h index 1bf9106..3759f94 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -441,7 +441,7 @@ struct zapi_nexthop { struct ethaddr rmac; - uint32_t weight; + uint64_t weight; /* Backup nexthops, for IP-FRR, TI-LFA, etc */ uint8_t backup_num; @@ -50,6 +50,7 @@ #include "printfrr.h" #include "frrcu.h" #include "zlog.h" +#include "zlog_live.h" #include "libfrr_trace.h" #include "frrevent.h" @@ -109,6 +110,9 @@ struct zlog_msg { size_t textlen; size_t hdrlen; + /* for relayed log messages ONLY (cf. zlog_recirculate_live_msg) */ + intmax_t pid, tid; + /* This is always ISO8601 with sub-second precision 9 here, it's * converted for callers as needed. ts_dot points to the "." * separating sub-seconds. ts_zonetail is "Z" or "+00:00" for the @@ -357,6 +361,16 @@ void zlog_msg_pid(struct zlog_msg *msg, intmax_t *pid, intmax_t *tid) { #ifndef __OpenBSD__ static thread_local intmax_t cached_pid = -1; +#endif + + /* recirculated messages */ + if (msg->pid) { + *pid = msg->pid; + *tid = msg->tid; + return; + } + +#ifndef __OpenBSD__ if (cached_pid != -1) *pid = cached_pid; else @@ -507,6 +521,89 @@ static void vzlog_tls(struct zlog_tls *zlog_tls, const struct xref_logmsg *xref, XFREE(MTYPE_LOG_MESSAGE, msg->text); } +/* reinject log message received by zlog_recirculate_recv(). As of writing, + * only used in the ldpd parent process to proxy messages from lde/ldpe + * subprocesses. + */ +void zlog_recirculate_live_msg(uint8_t *data, size_t len) +{ + struct zlog_target *zt; + struct zlog_msg stackmsg = {}, *msg = &stackmsg; + struct zlog_live_hdr *hdr; + struct xrefdata *xrefdata, ref = {}; + + if (len < sizeof(*hdr)) + return; + + hdr = (struct zlog_live_hdr *)data; + if (hdr->hdrlen < sizeof(*hdr)) + return; + data += hdr->hdrlen; + len -= sizeof(*hdr); + + msg->ts.tv_sec = hdr->ts_sec; + msg->ts.tv_nsec = hdr->ts_nsec; + msg->pid = hdr->pid; + msg->tid = hdr->tid; + msg->prio = hdr->prio; + + if (hdr->textlen > len) + return; + msg->textlen = hdr->textlen; + msg->hdrlen = hdr->texthdrlen; + msg->text = (char *)data; + + /* caller needs to make sure we have a trailing \n\0, it's not + * transmitted on zlog_live + */ + if (msg->text[msg->textlen] != '\n' || + msg->text[msg->textlen + 1] != '\0') + return; + + static_assert(sizeof(msg->argpos[0]) == sizeof(hdr->argpos[0]), + "in-memory struct doesn't match on-wire variant"); + msg->n_argpos = MIN(hdr->n_argpos, array_size(msg->argpos)); + memcpy(msg->argpos, hdr->argpos, msg->n_argpos * sizeof(msg->argpos[0])); + + /* This will only work if we're in the same daemon: we received a log + * message uid and are now doing a lookup in *our* known uids to find + * it. This works for ldpd because it's the same binary containing the + * same log messages, and ldpd is the only use case right now. + * + * When the uid is not found, the log message uid is lost but the + * message itself is still processed correctly. If this is needed, + * this can be made to work in two ways: + * (a) synthesize a temporary xref_logmsg from the received data. + * This is a bit annoying due to lifetimes with per-thread buffers. + * (b) extract and aggregate all log messages. This already happens + * with frr.xref but that would need to be fed back in. + */ + strlcpy(ref.uid, hdr->uid, sizeof(ref.uid)); + xrefdata = xrefdata_uid_find(&xrefdata_uid, &ref); + + if (xrefdata && xrefdata->xref->type == XREFT_LOGMSG) { + struct xref_logmsg *xref_logmsg; + + xref_logmsg = (struct xref_logmsg *)xrefdata->xref; + msg->xref = xref_logmsg; + msg->fmt = xref_logmsg->fmtstring; + } else { + /* fake out format string... */ + msg->fmt = msg->text + hdr->texthdrlen; + } + + rcu_read_lock(); + frr_each_safe (zlog_targets, &zlog_targets, zt) { + if (msg->prio > zt->prio_min) + continue; + if (!zt->logfn) + continue; + + zt->logfn(zt, &msg, 1); + } + rcu_read_unlock(); +} + static void zlog_backtrace_msg(const struct xref_logmsg *xref, int prio) { struct event *tc = pthread_getspecific(thread_current); @@ -125,6 +125,9 @@ static inline void zlog_ref(const struct xref_logmsg *xref, extern void zlog_sigsafe(const char *text, size_t len); +/* recirculate a log message from zlog_live */ +extern void zlog_recirculate_live_msg(uint8_t *data, size_t len); + /* extra priority value to disable a target without deleting it */ #define ZLOG_DISABLED (LOG_EMERG-1) diff --git a/lib/zlog_recirculate.c b/lib/zlog_recirculate.c new file mode 100644 index 0000000..abc73ee --- /dev/null +++ b/lib/zlog_recirculate.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (c) 2024 David Lamparter, for NetDEF, Inc. + */ + +#include "zebra.h" + +#include "log.h" +#include "frrevent.h" + +#include "zlog_recirculate.h" + +/* This is only the event loop part; it's split off from + * zlog_recirculate_live_msg since there's an integration boundary; this + * half deals with events, the other half with zlog interna. + * + * As of writing, this runs in ldpd in the *parent* process and receives log + * messages from the lde/ldpe subprocesses. It is not used anywhere else + * (yet?) + */ +static void zlog_recirculate_recv(struct event *ev) +{ + uint8_t rxbuf[4096]; + ssize_t n_rd; + int fd = EVENT_FD(ev); + + /* see below for -2, "\n\0" are added */ + n_rd = read(fd, rxbuf, sizeof(rxbuf) - 2); + if (n_rd == 0) { + /* EOF */ + close(fd); + /* event_add_read not called yet, nothing to cancel */ + return; + } + if (n_rd < 0 && (errno != EAGAIN) && (errno != EWOULDBLOCK)) { + /* error */ + zlog_warn("error on log relay socket %d: %m", fd); + close(fd); + /* event_add_read not called yet, nothing to cancel */ + return; + } + + event_add_read(ev->master, zlog_recirculate_recv, NULL, fd, NULL); + if (n_rd < 0) + return; + + /* log infrastructure has an implicit \n\0 at the end */ + rxbuf[n_rd] = '\n'; + rxbuf[n_rd + 1] = '\0'; + zlog_recirculate_live_msg(rxbuf, n_rd); +} + +void zlog_recirculate_subscribe(struct event_loop *el, int fd) +{ + event_add_read(el, zlog_recirculate_recv, NULL, fd, NULL); +} diff --git a/lib/zlog_recirculate.h b/lib/zlog_recirculate.h new file mode 100644 index 0000000..a2ddb4e --- /dev/null +++ b/lib/zlog_recirculate.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (c) 2024 David Lamparter, for NetDEF, Inc. + */ + +#ifndef _FRR_ZLOG_RECIRCULATE_H +#define _FRR_ZLOG_RECIRCULATE_H + +/* fd should be one end of a socketpair() */ +extern void zlog_recirculate_subscribe(struct event_loop *tm, int fd); + +#endif |