summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/affinitymap.c2
-rw-r--r--lib/agentx.c44
-rw-r--r--lib/atomlist.h2
-rw-r--r--lib/base64.c9
-rw-r--r--lib/base64.h1
-rw-r--r--lib/checksum.c2
-rw-r--r--lib/checksum.h2
-rw-r--r--lib/command.c22
-rw-r--r--lib/command.h3
-rw-r--r--lib/compiler.h6
-rw-r--r--lib/elf_py.c9
-rw-r--r--lib/explicit_bzero.c2
-rw-r--r--lib/filter.c18
-rw-r--r--lib/frr_pthread.c35
-rw-r--r--lib/frr_pthread.h11
-rw-r--r--lib/frr_zmq.c2
-rw-r--r--lib/if.c51
-rw-r--r--lib/ipaddr.h13
-rw-r--r--lib/keychain.c1011
-rw-r--r--lib/keychain.h38
-rw-r--r--lib/keychain_cli.c1033
-rw-r--r--lib/keychain_nb.c898
-rw-r--r--lib/lib_errors.c18
-rw-r--r--lib/lib_errors.h3
-rw-r--r--lib/libagentx.c63
-rw-r--r--lib/libagentx.h14
-rw-r--r--lib/libfrr.c48
-rw-r--r--lib/libfrr.h13
-rw-r--r--lib/libfrr_trace.c2
-rw-r--r--lib/libospf.h1
-rw-r--r--lib/link_state.c124
-rw-r--r--lib/link_state.h23
-rw-r--r--lib/memory.h27
-rw-r--r--lib/mgmt.proto1
-rw-r--r--lib/mgmt_be_client.c143
-rw-r--r--lib/mgmt_be_client.h2
-rw-r--r--lib/mgmt_fe_client.c99
-rw-r--r--lib/mgmt_fe_client.h80
-rw-r--r--lib/mgmt_msg_native.c5
-rw-r--r--lib/mgmt_msg_native.h125
-rw-r--r--lib/netns_other.c5
-rw-r--r--lib/nexthop.c343
-rw-r--r--lib/nexthop.h6
-rw-r--r--lib/northbound.c234
-rw-r--r--lib/northbound.h54
-rw-r--r--lib/northbound_cli.c75
-rw-r--r--lib/northbound_cli.h34
-rw-r--r--lib/northbound_confd.c1494
-rw-r--r--lib/northbound_grpc.cpp61
-rw-r--r--lib/northbound_oper.c39
-rw-r--r--lib/northbound_sysrepo.c67
-rw-r--r--lib/plist.c30
-rw-r--r--lib/prefix.c15
-rw-r--r--lib/prefix.h10
-rw-r--r--lib/printf/README1
-rw-r--r--lib/printf/printflocal.h1
-rw-r--r--lib/printf/vfprintf.c44
-rw-r--r--lib/routemap_cli.c47
-rw-r--r--lib/routing_nb.h2
-rw-r--r--lib/sigevent.c12
-rw-r--r--lib/smux.h1
-rw-r--r--lib/srv6.c4
-rw-r--r--lib/stream.c8
-rw-r--r--lib/stream.h4
-rw-r--r--lib/subdir.am22
-rw-r--r--lib/typerb.h2
-rw-r--r--lib/typesafe.h2
-rw-r--r--lib/vty.c146
-rw-r--r--lib/vty.h12
-rw-r--r--lib/yang.c196
-rw-r--r--lib/yang.h50
-rw-r--r--lib/yang_wrappers.c56
-rw-r--r--lib/yang_wrappers.h10
-rw-r--r--lib/zclient.c42
-rw-r--r--lib/zclient.h2
-rw-r--r--lib/zlog.c97
-rw-r--r--lib/zlog.h3
-rw-r--r--lib/zlog_recirculate.c56
-rw-r--r--lib/zlog_recirculate.h12
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 */
diff --git a/lib/if.c b/lib/if.c
index a8ceac7..8f15230 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -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, &params[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);
diff --git a/lib/smux.h b/lib/smux.h
index cec4d2a..8ec847a 100644
--- a/lib/smux.h
+++ b/lib/smux.h
@@ -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,
diff --git a/lib/srv6.c b/lib/srv6.c
index dceb6ab..a82103e 100644
--- a/lib/srv6.c
+++ b/lib/srv6.c
@@ -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" {
diff --git a/lib/vty.c b/lib/vty.c
index 1c9cff4..d0bbf0e 100644
--- a/lib/vty.c
+++ b/lib/vty.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)
{
diff --git a/lib/vty.h b/lib/vty.h
index a59ac7a..c336a81 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -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);
diff --git a/lib/yang.c b/lib/yang.c
index 03044fc..44459df 100644
--- a/lib/yang.c
+++ b/lib/yang.c
@@ -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
*/
diff --git a/lib/yang.h b/lib/yang.h
index 65f6a73..57131f4 100644
--- a/lib/yang.h
+++ b/lib/yang.h
@@ -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, &times);
+ 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;
diff --git a/lib/zlog.c b/lib/zlog.c
index 8734fd5..ffca572 100644
--- a/lib/zlog.c
+++ b/lib/zlog.c
@@ -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);
diff --git a/lib/zlog.h b/lib/zlog.h
index 27401d5..9d93979 100644
--- a/lib/zlog.h
+++ b/lib/zlog.h
@@ -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