summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/.gitignore2
-rw-r--r--lib/affinitymap.c46
-rw-r--r--lib/affinitymap.h12
-rw-r--r--lib/affinitymap_cli.c44
-rw-r--r--lib/affinitymap_northbound.c30
-rw-r--r--lib/agentx.c22
-rw-r--r--lib/asn.h8
-rw-r--r--lib/base64.c22
-rw-r--r--lib/base64.h1
-rw-r--r--lib/bfd.c8
-rw-r--r--lib/bitfield.h7
-rw-r--r--lib/buffer.h26
-rw-r--r--lib/checksum.c2
-rw-r--r--lib/checksum.h2
-rw-r--r--lib/command.c95
-rw-r--r--lib/command.h24
-rw-r--r--lib/command_match.c6
-rw-r--r--lib/command_match.h5
-rw-r--r--lib/compiler.h24
-rw-r--r--lib/config_paths.h.in24
-rw-r--r--lib/cspf.c5
-rw-r--r--lib/darr.c159
-rw-r--r--lib/darr.h753
-rw-r--r--lib/defun_lex.l1
-rw-r--r--lib/distribute.c153
-rw-r--r--lib/distribute.h39
-rw-r--r--lib/elf_py.c6
-rw-r--r--lib/event.c384
-rw-r--r--lib/explicit_bzero.c2
-rw-r--r--lib/filter.c15
-rw-r--r--lib/filter.h6
-rw-r--r--lib/filter_cli.c373
-rw-r--r--lib/filter_nb.c225
-rw-r--r--lib/flex_algo.c93
-rw-r--r--lib/flex_algo.h10
-rw-r--r--lib/frr_pthread.c38
-rw-r--r--lib/frr_pthread.h3
-rw-r--r--lib/frrcu.c36
-rw-r--r--lib/frrcu.h6
-rw-r--r--lib/frrdistance.h35
-rw-r--r--lib/frrevent.h24
-rw-r--r--lib/frrlua.c60
-rw-r--r--lib/frrlua.h41
-rw-r--r--lib/frrscript.c5
-rw-r--r--lib/frrscript.h23
-rw-r--r--lib/frrsendmmsg.h30
-rw-r--r--lib/frrstr.c45
-rw-r--r--lib/frrstr.h14
-rw-r--r--lib/grammar_sandbox.c10
-rw-r--r--lib/hash.h6
-rw-r--r--lib/hook.h2
-rw-r--r--lib/if.c145
-rw-r--r--lib/if.h95
-rw-r--r--lib/if_rmap.c12
-rw-r--r--lib/imsg-buffer.c1
-rw-r--r--lib/ipaddr.h1
-rw-r--r--lib/jhash.c22
-rw-r--r--lib/keychain.c1008
-rw-r--r--lib/keychain.h39
-rw-r--r--lib/keychain_cli.c1033
-rw-r--r--lib/keychain_nb.c898
-rw-r--r--lib/ldp_sync.h2
-rw-r--r--lib/lib_errors.c18
-rw-r--r--lib/lib_errors.h3
-rw-r--r--lib/libfrr.c285
-rw-r--r--lib/libfrr.h74
-rw-r--r--lib/libfrr_trace.c2
-rw-r--r--lib/libospf.h8
-rw-r--r--lib/link_state.c80
-rw-r--r--lib/log.c27
-rw-r--r--lib/log_vty.c92
-rw-r--r--lib/mgmt.proto115
-rw-r--r--lib/mgmt_be_client.c1237
-rw-r--r--lib/mgmt_be_client.h239
-rw-r--r--lib/mgmt_defines.h38
-rw-r--r--lib/mgmt_fe_client.c1180
-rw-r--r--lib/mgmt_fe_client.h303
-rw-r--r--lib/mgmt_msg.c627
-rw-r--r--lib/mgmt_msg.h162
-rw-r--r--lib/mgmt_msg_native.c48
-rw-r--r--lib/mgmt_msg_native.h585
-rw-r--r--lib/mlag.c10
-rw-r--r--lib/mlag.h6
-rw-r--r--lib/netns_linux.c8
-rw-r--r--lib/netns_other.c5
-rw-r--r--lib/network.c1
-rw-r--r--lib/nexthop.c128
-rw-r--r--lib/nexthop.h13
-rw-r--r--lib/nexthop_group.c28
-rw-r--r--lib/nexthop_group.h4
-rw-r--r--lib/northbound.c1080
-rw-r--r--lib/northbound.h297
-rw-r--r--lib/northbound_cli.c91
-rw-r--r--lib/northbound_cli.h2
-rw-r--r--lib/northbound_confd.c1494
-rw-r--r--lib/northbound_grpc.cpp20
-rw-r--r--lib/northbound_oper.c1857
-rw-r--r--lib/northbound_sysrepo.c62
-rw-r--r--lib/ntop.c2
-rw-r--r--lib/pbr.h83
-rw-r--r--lib/pid_output.c1
-rw-r--r--lib/plist.c109
-rw-r--r--lib/prefix.c40
-rw-r--r--lib/prefix.h67
-rw-r--r--lib/printf/README7
-rw-r--r--lib/printf/glue.c6
-rw-r--r--lib/printf/printf-pos.c20
-rw-r--r--lib/printf/printfcommon.h20
-rw-r--r--lib/printf/printflocal.h3
-rw-r--r--lib/printf/vfprintf.c71
-rw-r--r--lib/privs.c9
-rw-r--r--lib/pullwr.c2
-rw-r--r--lib/resolver.c5
-rw-r--r--lib/resolver.h1
-rw-r--r--lib/route_types.txt4
-rw-r--r--lib/routemap.c71
-rw-r--r--lib/routemap.h11
-rw-r--r--lib/routemap_cli.c61
-rw-r--r--lib/routemap_northbound.c63
-rw-r--r--lib/routing_nb.c10
-rw-r--r--lib/routing_nb.h5
-rw-r--r--lib/routing_nb_config.c11
-rw-r--r--lib/sha256.c2
-rw-r--r--lib/sigevent.c14
-rw-r--r--lib/sockopt.c30
-rw-r--r--lib/sockunion.h35
-rw-r--r--lib/srv6.c6
-rw-r--r--lib/srv6.h85
-rw-r--r--lib/stream.h2
-rw-r--r--lib/subdir.am40
-rw-r--r--lib/systemd.c1
-rw-r--r--lib/table.c1
-rw-r--r--lib/typesafe.h17
-rw-r--r--lib/vector.c1
-rw-r--r--lib/vrf.c173
-rw-r--r--lib/vrf.h18
-rw-r--r--lib/vty.c1123
-rw-r--r--lib/vty.h67
-rw-r--r--lib/wheel.c2
-rw-r--r--lib/workqueue.c30
-rw-r--r--lib/workqueue.h21
-rw-r--r--lib/yang.c522
-rw-r--r--lib/yang.h153
-rw-r--r--lib/yang_translator.c8
-rw-r--r--lib/yang_wrappers.c83
-rw-r--r--lib/yang_wrappers.h17
-rw-r--r--lib/zclient.c702
-rw-r--r--lib/zclient.h165
-rw-r--r--lib/zebra.h119
-rw-r--r--lib/zlog.c117
-rw-r--r--lib/zlog.h4
-rw-r--r--lib/zlog_5424.c25
-rw-r--r--lib/zlog_live.c2
-rw-r--r--lib/zlog_recirculate.c56
-rw-r--r--lib/zlog_recirculate.h12
-rw-r--r--lib/zlog_targets.c1
156 files changed, 13619 insertions, 7214 deletions
diff --git a/lib/.gitignore b/lib/.gitignore
index 6176b30f8d..1c9314bf0d 100644
--- a/lib/.gitignore
+++ b/lib/.gitignore
@@ -1,3 +1,4 @@
+/config_paths.h
/version.c
/version.h
/gitversion.h
@@ -11,3 +12,4 @@
/grammar_sandbox
/clippy
/defun_lex.c
+vtysh_daemons.h
diff --git a/lib/affinitymap.c b/lib/affinitymap.c
index 17e1b2cc01..6ff8e83f91 100644
--- a/lib/affinitymap.c
+++ b/lib/affinitymap.c
@@ -41,13 +41,11 @@
#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);
-struct affinity_maps affinity_map_master = {NULL, NULL, NULL, NULL};
+struct affinity_maps affinity_map_master = {NULL, NULL};
static void affinity_map_free(struct affinity_map *map)
{
@@ -106,36 +104,6 @@ struct affinity_map *affinity_map_get(const char *name)
return NULL;
}
-
-char *affinity_map_name_get(int pos)
-{
- struct listnode *node;
- struct affinity_map *map;
-
- if (!affinity_map_master.maps)
- return NULL;
-
- for (ALL_LIST_ELEMENTS_RO(affinity_map_master.maps, node, map))
- if (map->bit_position == pos)
- return map->name;
- return NULL;
-}
-
-bool affinity_map_check_use_hook(const char *affmap_name)
-{
- if (affinity_map_master.check_use_hook)
- return (*affinity_map_master.check_use_hook)(affmap_name);
- return false;
-}
-
-bool affinity_map_check_update_hook(const char *affmap_name, uint16_t new_pos)
-{
- if (affinity_map_master.check_update_hook)
- return (*affinity_map_master.check_update_hook)(affmap_name,
- new_pos);
- return true;
-}
-
void affinity_map_update_hook(const char *affmap_name, uint16_t new_pos)
{
struct affinity_map *map;
@@ -153,18 +121,6 @@ void affinity_map_update_hook(const char *affmap_name, uint16_t new_pos)
new_pos);
}
-
-void affinity_map_set_check_use_hook(bool (*func)(const char *affmap_name))
-{
- affinity_map_master.check_use_hook = func;
-}
-
-void affinity_map_set_check_update_hook(bool (*func)(const char *affmap_name,
- uint16_t new_pos))
-{
- affinity_map_master.check_update_hook = func;
-}
-
void affinity_map_set_update_hook(void (*func)(const char *affmap_name,
uint16_t old_pos,
uint16_t new_pos))
diff --git a/lib/affinitymap.h b/lib/affinitymap.h
index 19edf5a269..ebe2659bf7 100644
--- a/lib/affinitymap.h
+++ b/lib/affinitymap.h
@@ -50,8 +50,6 @@ DECLARE_QOBJ_TYPE(affinity_map);
struct affinity_maps {
struct list *maps;
- bool (*check_use_hook)(const char *affmap_name);
- bool (*check_update_hook)(const char *affmap_name, uint16_t new_pos);
void (*update_hook)(const char *affmap_name, uint16_t old_pos,
uint16_t new_pos);
@@ -60,26 +58,18 @@ struct affinity_maps {
DECLARE_QOBJ_TYPE(affinity_maps);
extern const struct frr_yang_module_info frr_affinity_map_info;
+extern const struct frr_yang_module_info frr_affinity_map_cli_info;
void affinity_map_set(const char *name, int pos);
void affinity_map_unset(const char *name);
struct affinity_map *affinity_map_get(const char *name);
-char *affinity_map_name_get(const int pos);
-bool affinity_map_check_use_hook(const char *affmap_name);
-bool affinity_map_check_update_hook(const char *affmap_name, uint16_t new_pos);
void affinity_map_update_hook(const char *affmap_name, uint16_t new_pos);
-void affinity_map_set_check_use_hook(bool (*func)(const char *affmap_name));
-void affinity_map_set_check_update_hook(bool (*func)(const char *affmap_name,
- uint16_t new_pos));
void affinity_map_set_update_hook(void (*func)(const char *affmap_name,
uint16_t old_pos,
uint16_t new_pos));
-void cli_show_affinity_map(struct vty *vty, const struct lyd_node *dnode,
- bool show_defaults);
-
void affinity_map_init(void);
diff --git a/lib/affinitymap_cli.c b/lib/affinitymap_cli.c
index a2d5e8eccf..73b91e775b 100644
--- a/lib/affinitymap_cli.c
+++ b/lib/affinitymap_cli.c
@@ -30,15 +30,6 @@
#include "lib/affinitymap.h"
#include "lib/affinitymap_cli_clippy.c"
-/* Route map node structure. */
-static int affinity_map_config_write(struct vty *vty);
-static struct cmd_node affinitymap_node = {
- .name = "affinity-map",
- .node = AFFMAP_NODE,
- .prompt = "",
- .config_write = affinity_map_config_write,
-};
-
/* max value is EXT_ADMIN_GROUP_MAX_POSITIONS - 1 */
DEFPY_YANG_NOSH(affinity_map, affinity_map_cmd,
"affinity-map NAME$name bit-position (0-1023)$position",
@@ -75,33 +66,32 @@ DEFPY_YANG_NOSH(no_affinity_map, no_affinity_map_cmd,
return nb_cli_apply_changes(vty, NULL);
}
-static int affinity_map_config_write(struct vty *vty)
-{
- const struct lyd_node *dnode;
- int written = 0;
-
- dnode = yang_dnode_get(running_config->dnode, "/frr-affinity-map:lib");
- if (dnode) {
- nb_cli_show_dnode_cmds(vty, dnode, false);
- written = 1;
- }
-
- return written;
-}
-
-void cli_show_affinity_map(struct vty *vty, const struct lyd_node *dnode,
+static void cli_show_affinity_map(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults __attribute__((__unused__)))
{
vty_out(vty, "affinity-map %s bit-position %u\n",
- yang_dnode_get_string(dnode, "./name"),
- yang_dnode_get_uint16(dnode, "./value"));
+ yang_dnode_get_string(dnode, "name"),
+ yang_dnode_get_uint16(dnode, "value"));
}
+const struct frr_yang_module_info frr_affinity_map_cli_info = {
+ .name = "frr-affinity-map",
+ .ignore_cfg_cbs = true,
+ .nodes = {
+ {
+ .xpath = "/frr-affinity-map:lib/affinity-maps/affinity-map",
+ .cbs.cli_show = cli_show_affinity_map,
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
+
/* Initialization of affinity map vector. */
void affinity_map_init(void)
{
/* CLI commands. */
- install_node(&affinitymap_node);
install_element(CONFIG_NODE, &affinity_map_cmd);
install_element(CONFIG_NODE, &no_affinity_map_cmd);
}
diff --git a/lib/affinitymap_northbound.c b/lib/affinitymap_northbound.c
index 331075f5c1..003e0c11b9 100644
--- a/lib/affinitymap_northbound.c
+++ b/lib/affinitymap_northbound.c
@@ -47,11 +47,6 @@ static int lib_affinity_map_destroy(struct nb_cb_destroy_args *args)
switch (args->event) {
case NB_EV_VALIDATE:
- if (!affinity_map_check_use_hook(name))
- break;
- snprintf(args->errmsg, args->errmsg_len,
- "affinity-map %s is used", name);
- return NB_ERR_VALIDATION;
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
@@ -68,7 +63,6 @@ static int lib_affinity_map_destroy(struct nb_cb_destroy_args *args)
static int lib_affinity_map_value_modify(struct nb_cb_modify_args *args)
{
const char *name;
- char *map_name;
uint16_t pos;
name = yang_dnode_get_string(
@@ -79,20 +73,6 @@ static int lib_affinity_map_value_modify(struct nb_cb_modify_args *args)
switch (args->event) {
case NB_EV_VALIDATE:
- map_name = affinity_map_name_get(pos);
- if (map_name &&
- strncmp(map_name, name, AFFINITY_NAME_SIZE) != 0) {
- snprintf(args->errmsg, args->errmsg_len,
- "bit-position is used by %s.", map_name);
- return NB_ERR_VALIDATION;
- }
- if (!affinity_map_check_update_hook(name, pos)) {
- snprintf(
- args->errmsg, args->errmsg_len,
- "affinity-map new bit-position > 31 but is used with standard admin-groups");
- return NB_ERR_VALIDATION;
- }
- break;
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
@@ -105,11 +85,6 @@ static int lib_affinity_map_value_modify(struct nb_cb_modify_args *args)
return NB_OK;
}
-static int lib_affinity_map_value_destroy(struct nb_cb_destroy_args *args)
-{
- return NB_OK;
-}
-
/* clang-format off */
const struct frr_yang_module_info frr_affinity_map_info = {
.name = "frr-affinity-map",
@@ -119,14 +94,13 @@ const struct frr_yang_module_info frr_affinity_map_info = {
.cbs = {
.create = lib_affinity_map_create,
.destroy = lib_affinity_map_destroy,
- .cli_show = cli_show_affinity_map,
- }
+ },
+ .priority = NB_DFLT_PRIORITY - 1,
},
{
.xpath = "/frr-affinity-map:lib/affinity-maps/affinity-map/value",
.cbs = {
.modify = lib_affinity_map_value_modify,
- .destroy = lib_affinity_map_value_destroy,
}
},
{
diff --git a/lib/agentx.c b/lib/agentx.c
index 45f14c2703..70ee6753ff 100644
--- a/lib/agentx.c
+++ b/lib/agentx.c
@@ -4,12 +4,14 @@
*/
#include <zebra.h>
+#include <fcntl.h>
#ifdef SNMP_AGENTX
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include <net-snmp/agent/snmp_vars.h>
+#include <net-snmp/library/large_fd_set.h>
#include "command.h"
#include "smux.h"
@@ -43,7 +45,7 @@ static void agentx_timeout(struct event *t)
static void agentx_read(struct event *t)
{
- fd_set fds;
+ netsnmp_large_fd_set lfds;
int flags, new_flags = 0;
int nonblock = false;
struct listnode *ln = EVENT_ARG(t);
@@ -68,9 +70,9 @@ static void agentx_read(struct event *t)
flog_err(EC_LIB_SYSTEM_CALL, "Failed to set snmp fd non blocking: %s(%d)",
strerror(errno), errno);
- FD_ZERO(&fds);
- FD_SET(EVENT_FD(t), &fds);
- snmp_read(&fds);
+ netsnmp_large_fd_set_init(&lfds, FD_SETSIZE);
+ netsnmp_large_fd_setfd(t->u.fd, &lfds);
+ snmp_read2(&lfds);
/* Reset the flag */
if (!nonblock) {
@@ -85,6 +87,7 @@ static void agentx_read(struct event *t)
netsnmp_check_outstanding_agent_requests();
agentx_events_update();
+ netsnmp_large_fd_set_cleanup(&lfds);
}
static void agentx_events_update(void)
@@ -92,15 +95,15 @@ static void agentx_events_update(void)
int maxfd = 0;
int block = 1;
struct timeval timeout = {.tv_sec = 0, .tv_usec = 0};
- fd_set fds;
+ netsnmp_large_fd_set lfds;
struct listnode *ln;
struct event **thr;
int fd, thr_fd;
event_cancel(&timeout_thr);
- FD_ZERO(&fds);
- snmp_select_info(&maxfd, &fds, &timeout, &block);
+ netsnmp_large_fd_set_init(&lfds, FD_SETSIZE);
+ snmp_select_info2(&maxfd, &lfds, &timeout, &block);
if (!block) {
event_add_timer_tv(agentx_tm, agentx_timeout, NULL, &timeout,
@@ -118,7 +121,7 @@ static void agentx_events_update(void)
/* caught up */
if (thr_fd == fd) {
struct listnode *nextln = listnextnode(ln);
- if (!FD_ISSET(fd, &fds)) {
+ if (!netsnmp_large_fd_is_set(fd, &lfds)) {
event_cancel(thr);
XFREE(MTYPE_TMP, thr);
list_delete_node(events, ln);
@@ -128,7 +131,7 @@ static void agentx_events_update(void)
thr_fd = thr ? EVENT_FD(*thr) : -1;
}
/* need listener, but haven't hit one where it would be */
- else if (FD_ISSET(fd, &fds)) {
+ else if (netsnmp_large_fd_is_set(fd, &lfds)) {
struct listnode *newln;
thr = XCALLOC(MTYPE_TMP, sizeof(struct event *));
@@ -147,6 +150,7 @@ static void agentx_events_update(void)
list_delete_node(events, ln);
ln = nextln;
}
+ netsnmp_large_fd_set_cleanup(&lfds);
}
/* AgentX node. */
diff --git a/lib/asn.h b/lib/asn.h
index 81a42c658d..a7394fa52b 100644
--- a/lib/asn.h
+++ b/lib/asn.h
@@ -66,10 +66,10 @@ extern char *asn_asn2string(const as_t *as, char *buf, size_t len,
((mode == ASNOTATION_DOT) ? "%pASD" : \
((mode == ASNOTATION_DOTPLUS) ? "%pASE" : \
"%pASP"))
-#define ASN_FORMAT_SPACE(mode) \
- ((mode == ASNOTATION_DOT) ? "%10pASD" : \
- ((mode == ASNOTATION_DOTPLUS) ? "%10pASE" : \
- "%10pASP"))
+#define ASN_FORMAT_SPACE(mode) \
+ ((mode == ASNOTATION_DOT) \
+ ? "%11pASD" \
+ : ((mode == ASNOTATION_DOTPLUS) ? "%11pASE" : "%11pASP"))
/* for test */
extern void asn_relax_as_zero(bool relax);
diff --git a/lib/base64.c b/lib/base64.c
index 1507b0252b..00dd35ffb5 100644
--- a/lib/base64.c
+++ b/lib/base64.c
@@ -9,8 +9,8 @@
#endif
#include "base64.h"
+#include "compiler.h"
-static const int CHARS_PER_LINE = 72;
static const char *ENCODING =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -18,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)
@@ -41,6 +40,7 @@ int base64_encode_block(const char *plaintext_in, int length_in, char *code_out,
switch (state_in->step) {
while (1) {
+ fallthrough;
case step_A:
if (plainchar == plaintextend) {
state_in->result = result;
@@ -51,7 +51,7 @@ int base64_encode_block(const char *plaintext_in, int length_in, char *code_out,
result = (fragment & 0x0fc) >> 2;
*codechar++ = base64_encode_value(result);
result = (fragment & 0x003) << 4;
- /* fall through */
+ fallthrough;
case step_B:
if (plainchar == plaintextend) {
state_in->result = result;
@@ -62,7 +62,7 @@ int base64_encode_block(const char *plaintext_in, int length_in, char *code_out,
result |= (fragment & 0x0f0) >> 4;
*codechar++ = base64_encode_value(result);
result = (fragment & 0x00f) << 2;
- /* fall through */
+ fallthrough;
case step_C:
if (plainchar == plaintextend) {
state_in->result = result;
@@ -74,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 */
@@ -103,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;
}
@@ -146,6 +139,7 @@ int base64_decode_block(const char *code_in, int length_in, char *plaintext_out,
switch (state_in->step) {
while (1) {
+ fallthrough;
case step_a:
do {
if (codec == code_in+length_in) {
@@ -156,7 +150,7 @@ int base64_decode_block(const char *code_in, int length_in, char *plaintext_out,
fragmt = base64_decode_value(*codec++);
} while (fragmt < 0);
*plainc = (fragmt & 0x03f) << 2;
- /* fall through */
+ fallthrough;
case step_b:
do {
if (codec == code_in+length_in) {
@@ -168,7 +162,7 @@ int base64_decode_block(const char *code_in, int length_in, char *plaintext_out,
} while (fragmt < 0);
*plainc++ |= (fragmt & 0x030) >> 4;
*plainc = (fragmt & 0x00f) << 4;
- /* fall through */
+ fallthrough;
case step_c:
do {
if (codec == code_in+length_in) {
@@ -180,7 +174,7 @@ int base64_decode_block(const char *code_in, int length_in, char *plaintext_out,
} while (fragmt < 0);
*plainc++ |= (fragmt & 0x03c) >> 2;
*plainc = (fragmt & 0x003) << 6;
- /* fall through */
+ fallthrough;
case step_d:
do {
if (codec == code_in+length_in) {
diff --git a/lib/base64.h b/lib/base64.h
index 839f92aa7c..9bf4ace82f 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/bfd.c b/lib/bfd.c
index cc6d09a60f..2222bb9547 100644
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -541,7 +541,7 @@ static void _bfd_sess_remove(struct bfd_session_params *bsp)
/* Send request to remove any session. */
bsp->lastev = BSE_UNINSTALL;
- event_execute(bsglobal.tm, _bfd_sess_send, bsp, 0);
+ event_execute(bsglobal.tm, _bfd_sess_send, bsp, 0, NULL);
}
void bfd_sess_free(struct bfd_session_params **bsp)
@@ -894,7 +894,7 @@ int zclient_bfd_session_replay(ZAPI_CALLBACK_ARGS)
/* Ask for installation. */
bsp->lastev = BSE_INSTALL;
- event_execute(bsglobal.tm, _bfd_sess_send, bsp, 0);
+ event_execute(bsglobal.tm, _bfd_sess_send, bsp, 0, NULL);
}
return 0;
@@ -1282,7 +1282,6 @@ static bool bfd_source_cache_update(struct bfd_source_cache *source,
const struct zapi_nexthop *nh = &route->nexthops[nh_index];
const struct interface *interface;
const struct connected *connected;
- const struct listnode *node;
interface = if_lookup_by_index(nh->ifindex, nh->vrf_id);
if (interface == NULL) {
@@ -1291,8 +1290,7 @@ static bool bfd_source_cache_update(struct bfd_source_cache *source,
continue;
}
- for (ALL_LIST_ELEMENTS_RO(interface->connected, node,
- connected)) {
+ frr_each (if_connected_const, interface->connected, connected) {
if (source->address.family !=
connected->address->family)
continue;
diff --git a/lib/bitfield.h b/lib/bitfield.h
index c4e2cbe4e1..3fda627b74 100644
--- a/lib/bitfield.h
+++ b/lib/bitfield.h
@@ -114,7 +114,9 @@ DECLARE_MTYPE(BITFIELD);
(v).n += ((v).data[w] == WORD_MAX); \
if ((v).n == (v).m) { \
(v).m = (v).m + 1; \
- (v).data = realloc((v).data, (v).m * sizeof(word_t)); \
+ (v).data = XREALLOC(MTYPE_BITFIELD, (v).data, \
+ (v).m * sizeof(word_t)); \
+ (v).data[(v).m - 1] = 0; \
} \
} while (0)
@@ -188,7 +190,8 @@ bf_find_next_clear_bit_wrap(bitfield_t *v, word_t start_index, word_t max_index)
* will allocate additional space.
*/
v->m += 1;
- v->data = (word_t *)realloc(v->data, v->m * sizeof(word_t));
+ v->data = (word_t *)XREALLOC(MTYPE_BITFIELD, v->data,
+ v->m * sizeof(word_t));
v->data[v->m - 1] = 0;
return v->m * WORD_SIZE;
}
diff --git a/lib/buffer.h b/lib/buffer.h
index 5d98c31dbc..a0b82d2121 100644
--- a/lib/buffer.h
+++ b/lib/buffer.h
@@ -14,21 +14,21 @@ extern "C" {
/* Create a new buffer. Memory will be allocated in chunks of the given
size. If the argument is 0, the library will supply a reasonable
default size suitable for buffering socket I/O. */
-extern struct buffer *buffer_new(size_t);
+extern struct buffer *buffer_new(size_t size);
/* Free all data in the buffer. */
-extern void buffer_reset(struct buffer *);
+extern void buffer_reset(struct buffer *b);
/* This function first calls buffer_reset to release all buffered data.
Then it frees the struct buffer itself. */
-extern void buffer_free(struct buffer *);
+extern void buffer_free(struct buffer *b);
/* Add the given data to the end of the buffer. */
-extern void buffer_put(struct buffer *, const void *, size_t);
+extern void buffer_put(struct buffer *b, const void *p, size_t size);
/* Add a single character to the end of the buffer. */
-extern void buffer_putc(struct buffer *, uint8_t);
+extern void buffer_putc(struct buffer *b, uint8_t c);
/* Add a NUL-terminated string to the end of the buffer. */
-extern void buffer_putstr(struct buffer *, const char *);
+extern void buffer_putstr(struct buffer *b, const char *str);
/* Add given data, inline-expanding \n to \r\n */
extern void buffer_put_crlf(struct buffer *b, const void *p, size_t size);
@@ -36,10 +36,10 @@ extern void buffer_put_crlf(struct buffer *b, const void *p, size_t size);
single NUL-terminated string allocated using XMALLOC(MTYPE_TMP). Note
that this function does not alter the state of the buffer, so the data
is still inside waiting to be flushed. */
-char *buffer_getstr(struct buffer *);
+char *buffer_getstr(struct buffer *b);
/* Returns 1 if there is no pending data in the buffer. Otherwise returns 0. */
-int buffer_empty(struct buffer *);
+int buffer_empty(struct buffer *b);
typedef enum {
/* An I/O error occurred. The buffer should be destroyed and the
@@ -59,12 +59,12 @@ typedef enum {
/* Try to write this data to the file descriptor. Any data that cannot
be written immediately is added to the buffer queue. */
-extern buffer_status_t buffer_write(struct buffer *, int fd, const void *,
- size_t);
+extern buffer_status_t buffer_write(struct buffer *b, int fd, const void *p,
+ size_t size);
/* This function attempts to flush some (but perhaps not all) of
the queued data to the given file descriptor. */
-extern buffer_status_t buffer_flush_available(struct buffer *, int fd);
+extern buffer_status_t buffer_flush_available(struct buffer *b, int fd);
/* The following 2 functions (buffer_flush_all and buffer_flush_window)
are for use in lib/vty.c only. They should not be used elsewhere. */
@@ -72,7 +72,7 @@ extern buffer_status_t buffer_flush_available(struct buffer *, int fd);
/* Call buffer_flush_available repeatedly until either all data has been
flushed, or an I/O error has been encountered, or the operation would
block. */
-extern buffer_status_t buffer_flush_all(struct buffer *, int fd);
+extern buffer_status_t buffer_flush_all(struct buffer *b, int fd);
/* Attempt to write enough data to the given fd to fill a window of the
given width and height (and remove the data written from the buffer).
@@ -85,7 +85,7 @@ extern buffer_status_t buffer_flush_all(struct buffer *, int fd);
to return -1 (because the logic for handling the erase and more features
is too complicated to retry the write later).
*/
-extern buffer_status_t buffer_flush_window(struct buffer *, int fd, int width,
+extern buffer_status_t buffer_flush_window(struct buffer *b, int fd, int width,
int height, int erase, int no_more);
#ifdef __cplusplus
diff --git a/lib/checksum.c b/lib/checksum.c
index 6c5f06de45..b1ad813fd9 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 508c3f38a6..2856a0d809 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 97ea200ff4..0288ab7599 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -10,6 +10,10 @@
*/
#include <zebra.h>
+#include <sys/utsname.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
#include <lib/version.h>
#include "command.h"
@@ -31,12 +35,16 @@
#include "jhash.h"
#include "hook.h"
#include "lib_errors.h"
+#include "mgmt_be_client.h"
+#include "mgmt_fe_client.h"
#include "northbound_cli.h"
#include "network.h"
#include "routemap.h"
#include "frrscript.h"
+#include "lib/config_paths.h"
+
DEFINE_MTYPE_STATIC(LIB, HOST, "Host config");
DEFINE_MTYPE(LIB, COMPLETION, "Completion item");
@@ -475,33 +483,18 @@ static int config_write_host(struct vty *vty)
}
log_config_write(vty);
- /* print disable always, but enable only if default is flipped
- * => prep for future removal of compile-time knob
- */
if (!cputime_enabled)
vty_out(vty, "no service cputime-stats\n");
-#ifdef EXCLUDE_CPU_TIME
- else
- vty_out(vty, "service cputime-stats\n");
-#endif
if (!cputime_threshold)
vty_out(vty, "no service cputime-warning\n");
-#if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000
- else /* again, always print non-default */
-#else
- else if (cputime_threshold != 5000000)
-#endif
+ else if (cputime_threshold != CONSUMED_TIME_CHECK)
vty_out(vty, "service cputime-warning %lu\n",
cputime_threshold / 1000);
if (!walltime_threshold)
vty_out(vty, "no service walltime-warning\n");
-#if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000
- else /* again, always print non-default */
-#else
- else if (walltime_threshold != 5000000)
-#endif
+ else if (walltime_threshold != CONSUMED_TIME_CHECK)
vty_out(vty, "service walltime-warning %lu\n",
walltime_threshold / 1000);
@@ -685,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)
{
@@ -701,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;
}
@@ -733,9 +744,13 @@ char *cmd_variable_comp2str(vector comps, unsigned short cols)
char *item = vector_slot(comps, j);
itemlen = strlen(item);
- if (cs + itemlen + AUTOCOMP_INDENT + 3 >= bsz)
- buf = XREALLOC(MTYPE_TMP, buf, (bsz *= 2));
+ size_t next_sz = cs + itemlen + AUTOCOMP_INDENT + 3;
+ if (next_sz > bsz) {
+ /* Make sure the buf size is large enough */
+ bsz = next_sz;
+ buf = XREALLOC(MTYPE_TMP, buf, bsz);
+ }
if (lc + itemlen + 1 >= cols) {
cs += snprintf(&buf[cs], bsz - cs, "\n%*s",
AUTOCOMP_INDENT, "");
@@ -756,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);
}
@@ -897,8 +912,7 @@ enum node_type node_parent(enum node_type node)
}
/* Execute command by argument vline vector. */
-static int cmd_execute_command_real(vector vline, enum cmd_filter_type filter,
- struct vty *vty,
+static int cmd_execute_command_real(vector vline, struct vty *vty,
const struct cmd_element **cmd,
unsigned int up_level)
{
@@ -1035,8 +1049,7 @@ int cmd_execute_command(vector vline, struct vty *vty,
vector_set_index(shifted_vline, index - 1,
vector_lookup(vline, index));
- ret = cmd_execute_command_real(shifted_vline, FILTER_RELAXED,
- vty, cmd, 0);
+ ret = cmd_execute_command_real(shifted_vline, vty, cmd, 0);
vector_free(shifted_vline);
vty->node = onode;
@@ -1045,7 +1058,7 @@ int cmd_execute_command(vector vline, struct vty *vty,
}
saved_ret = ret =
- cmd_execute_command_real(vline, FILTER_RELAXED, vty, cmd, 0);
+ cmd_execute_command_real(vline, vty, cmd, 0);
if (vtysh)
return saved_ret;
@@ -1063,8 +1076,7 @@ int cmd_execute_command(vector vline, struct vty *vty,
if (vty->xpath_index > 0 && !cnode->no_xpath)
vty->xpath_index--;
- ret = cmd_execute_command_real(vline, FILTER_RELAXED,
- vty, cmd, 0);
+ ret = cmd_execute_command_real(vline, vty, cmd, 0);
if (ret == CMD_SUCCESS || ret == CMD_WARNING
|| ret == CMD_ERR_AMBIGUOUS || ret == CMD_ERR_INCOMPLETE
|| ret == CMD_NOT_MY_INSTANCE
@@ -1096,7 +1108,7 @@ int cmd_execute_command(vector vline, struct vty *vty,
int cmd_execute_command_strict(vector vline, struct vty *vty,
const struct cmd_element **cmd)
{
- return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd, 0);
+ return cmd_execute_command_real(vline, vty, cmd, 0);
}
/*
@@ -1268,8 +1280,7 @@ int command_config_read_one_line(struct vty *vty,
&& ret != CMD_ERR_AMBIGUOUS && ret != CMD_ERR_INCOMPLETE
&& ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED
&& ret != CMD_NO_LEVEL_UP)
- ret = cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd,
- ++up_level);
+ ret = cmd_execute_command_real(vline, vty, cmd, ++up_level);
if (ret == CMD_NO_LEVEL_UP)
ret = CMD_ERR_NO_MATCH;
@@ -1281,6 +1292,7 @@ int command_config_read_one_line(struct vty *vty,
memcpy(ve->error_buf, vty->buf, VTY_BUFSIZ);
ve->line_num = line_num;
+ ve->cmd_ret = ret;
if (!vty->error)
vty->error = list_new();
@@ -1301,6 +1313,14 @@ int config_from_file(struct vty *vty, FILE *fp, unsigned int *line_num)
while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
++(*line_num);
+ if (vty_log_commands) {
+ int len = strlen(vty->buf);
+
+ /* now log the command */
+ zlog_notice("config-from-file# %.*s", len ? len - 1 : 0,
+ vty->buf);
+ }
+
ret = command_config_read_one_line(vty, NULL, *line_num, 0);
if (ret != CMD_SUCCESS && ret != CMD_WARNING
@@ -1318,11 +1338,12 @@ int config_from_file(struct vty *vty, FILE *fp, unsigned int *line_num)
/* Configuration from terminal */
DEFUN (config_terminal,
config_terminal_cmd,
- "configure [terminal]",
+ "configure [terminal [file-lock]]",
"Configuration from vty interface\n"
- "Configuration terminal\n")
+ "Configuration terminal\n"
+ "Configuration with locked datastores\n")
{
- return vty_config_enter(vty, false, false);
+ return vty_config_enter(vty, false, false, argc == 3);
}
/* Enable command */
@@ -1632,6 +1653,10 @@ static int vty_write_config(struct vty *vty)
return CMD_SUCCESS;
}
+/* cross-reference frr_daemon_state_save in libfrr.c
+ * the code there is similar but not identical (state files always use the same
+ * name for the new write, and don't keep a backup of previous state.)
+ */
static int file_write_config(struct vty *vty)
{
int fd, dirfd;
@@ -2438,6 +2463,8 @@ const char *host_config_get(void)
void cmd_show_lib_debugs(struct vty *vty)
{
route_map_show_debug(vty);
+ mgmt_debug_be_client_show_debug(vty);
+ mgmt_debug_fe_client_show_debug(vty);
}
void install_default(enum node_type node)
diff --git a/lib/command.h b/lib/command.h
index 39fbfa661a..ef1815c0bd 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -82,12 +82,16 @@ enum node_type {
AUTH_ENABLE_NODE, /* Authentication mode for change enable. */
ENABLE_NODE, /* Enable node. */
CONFIG_NODE, /* Config node. Default mode of config file. */
+ PREFIX_NODE, /* ip prefix-list node. */
+ PREFIX_IPV6_NODE, /* ipv6 prefix-list node. */
DEBUG_NODE, /* Debug node. */
VRF_DEBUG_NODE, /* Vrf Debug node. */
NORTHBOUND_DEBUG_NODE, /* Northbound Debug node. */
DEBUG_VNC_NODE, /* Debug VNC node. */
RMAP_DEBUG_NODE, /* Route-map debug node */
RESOLVER_DEBUG_NODE, /* Resolver debug node */
+ MGMT_BE_DEBUG_NODE, /* mgmtd backend-client debug node */
+ MGMT_FE_DEBUG_NODE, /* mgmtd frontend-client debug node */
AAA_NODE, /* AAA node. */
EXTLOG_NODE, /* RFC5424 & co. extended syslog */
KEYCHAIN_NODE, /* Key-chain node. */
@@ -131,10 +135,8 @@ enum node_type {
ISIS_NODE, /* ISIS protocol mode */
ISIS_FLEX_ALGO_NODE, /* ISIS Flex Algo mode */
ACCESS_NODE, /* Access list node. */
- PREFIX_NODE, /* Prefix list node. */
ACCESS_IPV6_NODE, /* Access list node. */
ACCESS_MAC_NODE, /* MAC access list node*/
- PREFIX_IPV6_NODE, /* Prefix list node. */
AS_LIST_NODE, /* AS list node. */
COMMUNITY_LIST_NODE, /* Community list node. */
COMMUNITY_ALIAS_NODE, /* Community alias node. */
@@ -158,6 +160,7 @@ enum node_type {
SRV6_NODE, /* SRv6 node */
SRV6_LOCS_NODE, /* SRv6 locators node */
SRV6_LOC_NODE, /* SRv6 locator node */
+ SRV6_ENCAP_NODE, /* SRv6 encapsulation node */
VTY_NODE, /* Vty node. */
FPM_NODE, /* Dataplane FPM node. */
LINK_PARAMS_NODE, /* Link-parameters node */
@@ -172,6 +175,10 @@ enum node_type {
OPENFABRIC_NODE, /* OpenFabric router configuration node */
VRRP_NODE, /* VRRP node */
BMP_NODE, /* BMP config under router bgp */
+ ISIS_SRV6_NODE, /* ISIS SRv6 node */
+ ISIS_SRV6_NODE_MSD_NODE, /* ISIS SRv6 Node MSDs node */
+ MGMTD_NODE, /* MGMTD node. */
+ RPKI_VRF_NODE, /* RPKI node for VRF */
NODE_TYPE_MAX, /* maximum */
};
/* clang-format on */
@@ -284,6 +291,10 @@ struct cmd_node {
#define DEFPY_YANG(funcname, cmdname, cmdstr, helpstr) \
DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG)
+#define DEFPY_YANG_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
+ DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, \
+ CMD_ATTR_YANG | CMD_ATTR_HIDDEN)
+
#define DEFPY_YANG_NOSH(funcname, cmdname, cmdstr, helpstr) \
DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, \
CMD_ATTR_YANG | CMD_ATTR_NOSH)
@@ -308,6 +319,10 @@ struct cmd_node {
#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \
DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_NOSH)
+#define DEFUN_YANG_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, \
+ CMD_ATTR_YANG | CMD_ATTR_HIDDEN)
+
#define DEFUN_YANG_NOSH(funcname, cmdname, cmdstr, helpstr) \
DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, \
CMD_ATTR_YANG | CMD_ATTR_NOSH)
@@ -416,6 +431,10 @@ struct cmd_node {
#define COMMUNITY_AANN_STR "Community number where AA and NN are (0-65535)\n"
#define COMMUNITY_VAL_STR \
"Community number in AA:NN format (where AA and NN are (0-65535)) or local-AS|no-advertise|no-export|internet|graceful-shutdown|accept-own-nexthop|accept-own|route-filter-translated-v4|route-filter-v4|route-filter-translated-v6|route-filter-v6|llgr-stale|no-llgr|blackhole|no-peer or additive\n"
+#define EXTCOMM_LIST_CMD_STR "<(1-99)|(100-500)|EXTCOMMUNITY_LIST_NAME>"
+#define EXTCOMM_STD_LIST_NUM_STR "Extended community-list number (standard)\n"
+#define EXTCOMM_EXP_LIST_NUM_STR "Extended community-list number (expanded)\n"
+#define EXTCOMM_LIST_NAME_STR "Extended community-list name\n"
#define MPLS_TE_STR "MPLS-TE specific commands\n"
#define LINK_PARAMS_STR "Configure interface link parameters\n"
#define OSPF_RI_STR "OSPF Router Information specific commands\n"
@@ -617,6 +636,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/command_match.c b/lib/command_match.c
index ff3c48fc31..97e6aeb469 100644
--- a/lib/command_match.c
+++ b/lib/command_match.c
@@ -405,10 +405,10 @@ enum matcher_rv command_complete(struct graph *graph, vector vline,
listnode_add(next, newstack);
break;
case partly_match:
- trace_matcher("trivial_match\n");
+ trace_matcher("partly_match\n");
if (exact_match_exists && !last_token)
break;
- /* fallthru */
+ fallthrough;
case exact_match:
trace_matcher("exact_match\n");
if (last_token) {
@@ -566,9 +566,9 @@ static int score_precedence(enum cmd_token_type type)
case IPV6_PREFIX_TKN:
case MAC_TKN:
case MAC_PREFIX_TKN:
- case ASNUM_TKN:
case RANGE_TKN:
return 2;
+ case ASNUM_TKN:
case WORD_TKN:
return 3;
case VARIABLE_TKN:
diff --git a/lib/command_match.h b/lib/command_match.h
index db2a8535e0..3e7a549e1d 100644
--- a/lib/command_match.h
+++ b/lib/command_match.h
@@ -17,11 +17,6 @@
extern "C" {
#endif
-/* These definitions exist in command.c in the current engine but should be
- * relocated here in the new engine
- */
-enum cmd_filter_type { FILTER_RELAXED, FILTER_STRICT };
-
/* matcher result value */
enum matcher_rv {
MATCHER_NO_MATCH,
diff --git a/lib/compiler.h b/lib/compiler.h
index 29fcfbefbf..03261052a2 100644
--- a/lib/compiler.h
+++ b/lib/compiler.h
@@ -32,8 +32,8 @@ extern "C" {
#if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 5)
# define _RET_NONNULL , returns_nonnull
#endif
-#if __has_attribute(fallthrough)
-# define _FALLTHROUGH __attribute__((fallthrough));
+#if __has_attribute(fallthrough) && !defined(__cplusplus)
+# define fallthrough __attribute__((fallthrough));
#endif
# define _CONSTRUCTOR(x) constructor(x)
# define _DEPRECATED(x) deprecated(x)
@@ -56,8 +56,8 @@ extern "C" {
#if __GNUC__ < 5
# define __has_attribute(x) 0
#endif
-#if __GNUC__ >= 7
-# define _FALLTHROUGH __attribute__((fallthrough));
+#if __GNUC__ >= 7 && !defined(__cplusplus)
+# define fallthrough __attribute__((fallthrough));
#endif
#endif
@@ -112,8 +112,8 @@ extern "C" {
#ifndef _ALLOC_SIZE
# define _ALLOC_SIZE(x)
#endif
-#ifndef _FALLTHROUGH
-#define _FALLTHROUGH
+#if !defined(fallthrough) && !defined(__cplusplus)
+#define fallthrough
#endif
#ifndef _DEPRECATED
#define _DEPRECATED(x) deprecated
@@ -122,6 +122,14 @@ extern "C" {
#define assume(x)
#endif
+#ifdef __COVERITY__
+/* __coverity_panic__() is named a bit poorly, it's essentially the same as
+ * __builtin_unreachable(). Used to eliminate false positives.
+ */
+#undef assume
+#define assume(x) do { if (!(x)) __coverity_panic__(); } while (0)
+#endif
+
/* for helper functions defined inside macros */
#define macro_inline static inline __attribute__((unused))
#define macro_pure static inline __attribute__((unused, pure))
@@ -416,10 +424,10 @@ _Static_assert(sizeof(_uint64_t) == 8 && sizeof(_int64_t) == 8,
* type.)
*/
#ifndef __cplusplus
-#define prefixtype(uname, typename, fieldname) typename *fieldname;
+#define uniontype(uname, typename, fieldname) typename *fieldname;
#define TRANSPARENT_UNION __attribute__((transparent_union))
#else
-#define prefixtype(uname, typename, fieldname) \
+#define uniontype(uname, typename, fieldname) \
typename *fieldname; \
uname(typename *x) \
{ \
diff --git a/lib/config_paths.h.in b/lib/config_paths.h.in
new file mode 100644
index 0000000000..cc40905632
--- /dev/null
+++ b/lib/config_paths.h.in
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* autogenerated by configure / config.status */
+
+/* IF YOU ARE INCLUDING THIS FILE FROM A DAEMON OR ZEBRA, YOU ARE PROBABLY
+ * DOING SOMETHING WRONG. Check for / add a library function that retrieves
+ * the path you need.
+ *
+ * Only libfrr and watchfrr should be including this file.
+ */
+
+/* the replacements for these are emitted by AX_SUBST_EXPANDED, which also
+ * adds the e_ prefix
+ */
+#define FRR_RUNSTATE_PATH "@e_frr_runstatedir@"
+#define FRR_LIBSTATE_PATH "@e_frr_libstatedir@"
+#define YANG_MODELS_PATH "@e_yangmodelsdir@"
+#define MODULE_PATH "@e_moduledir@"
+#define SCRIPT_PATH "@e_scriptdir@"
+
+/* for extra footgunning, this one has a trailing slash */
+#define SYSCONFDIR "@e_frr_sysconfdir@/"
+
+#define VTYSH_BIN_PATH "@e_vtysh_bin@"
+#define WATCHFRR_SH_PATH "@e_watchfrr_sh@"
diff --git a/lib/cspf.c b/lib/cspf.c
index 6a0fb7f63c..c17d8e0929 100644
--- a/lib/cspf.c
+++ b/lib/cspf.c
@@ -331,6 +331,8 @@ void cspf_clean(struct cspf *algo)
if (processed_count(&algo->processed)) {
frr_each_safe (processed, &algo->processed, path) {
processed_del(&algo->processed, path);
+ if (path == algo->pdst)
+ algo->pdst = NULL;
cpath_del(path);
}
}
@@ -343,6 +345,9 @@ void cspf_clean(struct cspf *algo)
}
}
+ if (algo->pdst)
+ cpath_del(algo->pdst);
+
memset(&algo->csts, 0, sizeof(struct constraints));
algo->path = NULL;
algo->pdst = NULL;
diff --git a/lib/darr.c b/lib/darr.c
new file mode 100644
index 0000000000..7a01274104
--- /dev/null
+++ b/lib/darr.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * June 23 2023, Christian Hopps <chopps@labn.net>
+ *
+ * Copyright (c) 2023, LabN Consulting, L.L.C.
+ *
+ */
+#include <zebra.h>
+#include "darr.h"
+#include "memory.h"
+
+DEFINE_MTYPE(LIB, DARR, "Dynamic Array");
+DEFINE_MTYPE(LIB, DARR_STR, "Dynamic Array String");
+
+static uint _msb(uint count)
+{
+ uint bit = 0;
+ int msb = 0;
+
+ while (count) {
+ if (count & 1)
+ msb = bit;
+ count >>= 1;
+ bit += 1;
+ }
+ return msb;
+}
+
+static uint darr_next_count(uint count, size_t esize)
+{
+ uint ncount;
+
+ if (esize > sizeof(long long) && count == 1)
+ /* treat like a pointer */
+ ncount = 1;
+ else {
+ uint msb = _msb(count);
+
+ ncount = 1ull << msb;
+ /* if the users count wasn't a pow2 make it the next pow2. */
+ if (ncount != count) {
+ assert(ncount < count);
+ ncount <<= 1;
+ if (esize < sizeof(long long) && ncount < 8)
+ ncount = 8;
+ }
+ }
+ return ncount;
+}
+
+static size_t darr_size(uint count, size_t esize)
+{
+ return count * esize + sizeof(struct darr_metadata);
+}
+
+char *__darr_in_vsprintf(char **sp, bool concat, const char *fmt, va_list ap)
+{
+ size_t inlen = concat ? darr_strlen(*sp) : 0;
+ size_t capcount = strlen(fmt) + MIN(inlen + 64, 128);
+ ssize_t len;
+ va_list ap_copy;
+
+ darr_ensure_cap(*sp, capcount);
+
+ if (!concat)
+ darr_reset(*sp);
+
+ /* code below counts on having a NUL terminated string */
+ if (darr_len(*sp) == 0)
+ *darr_append(*sp) = 0;
+again:
+ va_copy(ap_copy, ap);
+ len = vsnprintf(darr_last(*sp), darr_avail(*sp) + 1, fmt, ap_copy);
+ va_end(ap_copy);
+ if (len < 0)
+ darr_in_strcat(*sp, fmt);
+ else if ((size_t)len <= darr_avail(*sp))
+ _darr_len(*sp) += len;
+ else {
+ darr_ensure_cap(*sp, darr_len(*sp) + (size_t)len);
+ goto again;
+ }
+ return *sp;
+}
+
+char *__darr_in_sprintf(char **sp, bool concat, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ (void)__darr_in_vsprintf(sp, concat, fmt, ap);
+ va_end(ap);
+ return *sp;
+}
+
+
+void *__darr_resize(void *a, uint count, size_t esize, struct memtype *mtype)
+{
+ uint ncount = darr_next_count(count, esize);
+ size_t osz = (a == NULL) ? 0 : darr_size(darr_cap(a), esize);
+ size_t sz = darr_size(ncount, esize);
+ struct darr_metadata *dm;
+
+ if (a) {
+ dm = XREALLOC(_darr_meta(a)->mtype, _darr_meta(a), sz);
+ if (sz > osz)
+ memset((char *)dm + osz, 0, sz - osz);
+ } else {
+ dm = XCALLOC(mtype, sz);
+ dm->mtype = mtype;
+ }
+ dm->cap = ncount;
+ return (void *)(dm + 1);
+}
+
+
+void *__darr_insert_n(void *a, uint at, uint count, size_t esize, bool zero,
+ struct memtype *mtype)
+{
+ struct darr_metadata *dm;
+ uint olen, nlen;
+
+ if (!a)
+ a = __darr_resize(NULL, at + count, esize, mtype);
+ dm = (struct darr_metadata *)a - 1;
+ olen = dm->len;
+
+ // at == 1
+ // count == 100
+ // olen == 2
+
+ /* see if the user is expanding first using `at` */
+ if (at >= olen)
+ nlen = at + count;
+ else
+ nlen = olen + count;
+
+ if (nlen > dm->cap) {
+ a = __darr_resize(a, nlen, esize, mtype);
+ dm = (struct darr_metadata *)a - 1;
+ }
+
+#define _a_at(i) ((char *)a + ((i)*esize))
+ if (at < olen)
+ memmove(_a_at(at + count), _a_at(at), esize * (olen - at));
+
+ dm->len = nlen;
+
+ if (zero) {
+ if (at >= olen) {
+ at -= olen;
+ count += olen;
+ }
+ memset(_a_at(at), 0, esize * count);
+ }
+
+ return a;
+#undef _a_at
+}
diff --git a/lib/darr.h b/lib/darr.h
new file mode 100644
index 0000000000..404869d9a2
--- /dev/null
+++ b/lib/darr.h
@@ -0,0 +1,753 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * June 23 2023, Christian Hopps <chopps@labn.net>
+ *
+ * Copyright (c) 2023, LabN Consulting, L.L.C.
+ */
+#ifndef _FRR_DARR_H_
+#define _FRR_DARR_H_
+
+/*
+ * API functions:
+ * ==============
+ * - darr_append
+ * - darr_append_mt
+ * - darr_append_n
+ * - darr_append_n_mt
+ * - darr_append_nz
+ * - darr_append_nz_mt
+ * - darr_cap
+ * - darr_ensure_avail
+ * - darr_ensure_avail_mt
+ * - darr_ensure_cap
+ * - darr_ensure_cap_mt
+ * - darr_ensure_i
+ * - darr_ensure_i_mt
+ * - darr_free
+ * - darr_insert
+ * - darr_insert_mt
+ * - darr_insertz
+ * - darr_insertz_mt
+ * - darr_insert_n
+ * - darr_insert_n_mt
+ * - darr_insert_nz
+ * - darr_insert_nz_mt
+ * - darr_last
+ * - darr_lasti
+ * - darr_len
+ * - darr_maxi
+ * - darr_pop
+ * - darr_push
+ * - darr_pushz
+ * - darr_remove
+ * - darr_remove_n
+ * - darr_reset
+ * - darr_setlen
+ *
+ * Iteration
+ * ---------
+ * - darr_foreach_i
+ * - darr_foreach_p
+ *
+ * String Utilities
+ * ----------------
+ * - darr_in_strcat_tail
+ * - darr_in_strcatf, darr_in_vstrcatf
+ * - darr_in_strdup
+ * - darr_in_strdup_cap
+ * - darr_in_sprintf, darr_in_vsprintf
+ * - darr_set_strlen
+ * - darr_strdup
+ * - darr_strdup_cap
+ * - darr_strlen
+ * - darr_strnul
+ * - darr_sprintf, darr_vsprintf
+ */
+/*
+ * A few assured items
+ *
+ * - DAs will never have capacity 0 unless they are NULL pointers.
+ */
+
+/*
+ * NOTE: valgrind by default enables a "length64" heuristic (among others) which
+ * identifies "interior-pointer" 8 bytes forward of a "start-pointer" as a
+ * "start-pointer". This should cause what normally would be "possibly-lost"
+ * errors to instead be definite for dynamic arrays. This is b/c the header is 8 bytes
+ */
+
+#include <zebra.h>
+#include <limits.h>
+#include "memory.h"
+
+DECLARE_MTYPE(DARR);
+DECLARE_MTYPE(DARR_STR);
+
+struct darr_metadata {
+ uint32_t len;
+ uint32_t cap;
+ struct memtype *mtype;
+};
+
+void *__darr_insert_n(void *a, uint at, uint count, size_t esize, bool zero,
+ struct memtype *mt);
+char *__darr_in_sprintf(char **sp, bool concat, const char *fmt, ...)
+ PRINTFRR(3, 4);
+char *__darr_in_vsprintf(char **sp, bool concat, const char *fmt, va_list ap)
+ PRINTFRR(3, 0);
+void *__darr_resize(void *a, uint count, size_t esize, struct memtype *mt);
+
+
+#define _darr_esize(A) sizeof((A)[0])
+#define darr_esize(A) sizeof((A)[0])
+#define _darr_len(A) _darr_meta(A)->len
+#define _darr_meta(A) (((struct darr_metadata *)(A)) - 1)
+#define _darr_resize_mt(A, C, MT) \
+ ({ (A) = __darr_resize(A, C, _darr_esize(A), MT); })
+#define _darr_resize(A, C) _darr_resize_mt(A, C, MTYPE_DARR)
+
+/* Get the current capacity of the array */
+#define darr_cap(A) (((A) == NULL) ? 0 : _darr_meta(A)->cap)
+
+/* Get the current available expansion space */
+#define darr_avail(A) (((A) == NULL) ? 0 : (darr_cap(A) - darr_len(A)))
+
+/* Get the largest possible index one can `darr_ensure_i` w/o resizing */
+#define darr_maxi(A) ((int)darr_cap(A) - 1)
+
+/**
+ * darr_len() - Get the current length of the array as a unsigned int.
+ * darr_ilen() - Get the current length of the array as an int.
+ *
+ * Args:
+ * A: The dynamic array, can be NULL.
+ *
+ * Return:
+ * The current length of the array.
+ */
+#define darr_len(A) (((A) == NULL) ? 0 : _darr_meta(A)->len)
+#define darr_ilen(A) (((A) == NULL) ? 0 : (ssize_t)_darr_meta(A)->len)
+
+/**
+ * darr_lasti() - Get the last element's index.
+ *
+ * Args:
+ * A: The dynamic array, can be NULL.
+ *
+ * Return:
+ * The current last element index, or -1 for none.
+ */
+#define darr_lasti(A) (darr_ilen(A) - 1)
+
+/**
+ * Set the current length of the array `A` to 0.
+ *
+ * Args:
+ * A: The dynamic array, can be NULL.
+ */
+#define darr_reset(A) \
+ do { \
+ if ((A)) \
+ _darr_len(A) = 0; \
+ } while (0)
+
+/**
+ * Set the current length of the array `A` to `L`.
+ *
+ * This function does *not* guarantee the memory is valid to L,
+ * use `darr_ensure` or `darr_ensure_cap` for that.
+ *
+ * Args:
+ * A: The dynamic array, can only be NULL if (L) == 0.
+ * L: The new length of the array.
+ */
+#define darr_setlen(A, L) \
+ do { \
+ assert((A) || !(L)); \
+ if ((A)) { \
+ /* have to cast to avoid compiler warning for "0" */ \
+ assert((long long)darr_cap(A) >= (long long)(L)); \
+ _darr_len(A) = (L); \
+ } \
+ } while (0)
+
+/**
+ * Set the string length of the array `S` to `L`, and NUL
+ * terminate the string at L. The dynamic array length will be `L` + 1.
+ *
+ * Thus after calling:
+ *
+ * darr_len(S) == L + 1
+ * darr_strlen(S) == L
+ * S[L] == 0
+ *
+ * This function does *not* guarantee the `L` + 1 memory is allocated to
+ * the array, use `darr_ensure` or `*_cap` functions for that.
+ *
+ * Args:
+ * S: The dynamic array, cannot be NULL.
+ * L: The new str length of the array, will set
+ *
+ * Return:
+ * A pointer to the end of S (i.e., pointing to the NUL byte).
+ */
+#define darr_set_strlen(S, L) \
+ ({ \
+ assert((S)); \
+ /* have to cast to avoid compiler warning for "0" */ \
+ assert((long long)darr_cap(S) >= (long long)(L)); \
+ _darr_len(S) = (L) + 1; \
+ *darr_last(S) = 0; \
+ darr_last(S); \
+ })
+
+/**
+ * Free memory allocated for the dynamic array `A`
+ *
+ * Args:
+ * A: The dynamic array, can be NULL.
+ */
+
+#define darr_free(A) \
+ do { \
+ if ((A)) { \
+ struct darr_metadata *__meta = _darr_meta(A); \
+ XFREE(__meta->mtype, __meta); \
+ (A) = NULL; \
+ } \
+ } while (0)
+
+/**
+ * Make sure that there is room in the dynamic array `A` to add `C` elements.
+ *
+ * Available space is `darr_cap(a) - darr_len(a)`.
+ *
+ * The value `A` may be changed as a result of this call in which case any
+ * pointers into the previous memory block are no longer valid. The `A` value
+ * is guaranteed not to change if there is sufficient capacity in the array.
+ *
+ * Args:
+ * A: (IN/OUT) the dynamic array, can be NULL.
+ * S: Amount of free space to guarantee.
+ *
+ * Return:
+ * A pointer to the (possibly moved) array.
+ */
+#define darr_ensure_avail_mt(A, S, MT) \
+ ({ \
+ ssize_t need = (ssize_t)(S) - \
+ (ssize_t)(darr_cap(A) - darr_len(A)); \
+ if (need > 0) \
+ _darr_resize_mt((A), darr_cap(A) + need, MT); \
+ (A); \
+ })
+#define darr_ensure_avail(A, S) darr_ensure_avail_mt(A, S, MTYPE_DARR)
+
+/**
+ * Make sure that there is room in the dynamic array `A` for `C` elements.
+ *
+ * The value `A` may be changed as a result of this call in which case any
+ * pointers into the previous memory block are no longer valid. The `A` value
+ * is guaranteed not to change if there is sufficient capacity in the array.
+ *
+ * The exception to the no-change rule is if @C is passed as 0, it will be
+ * considered 1 so that an array is always allocated if currently NULL,
+ * i.e., @A will never be NULL after a call to darr_ensure_cap_mt()
+ *
+ * Args:
+ * A: (IN/OUT) the dynamic array, can be NULL.
+ * C: Total capacity to guarantee.
+ *
+ * Return:
+ * A pointer to the (possibly moved) array.
+ */
+#define darr_ensure_cap_mt(A, C, MT) \
+ ({ \
+ /* Cast to avoid warning when C == 0 */ \
+ uint _c = (C) > 0 ? (C) : 1; \
+ if ((size_t)darr_cap(A) < _c) \
+ _darr_resize_mt((A), _c, MT); \
+ (A); \
+ })
+#define darr_ensure_cap(A, C) darr_ensure_cap_mt(A, C, MTYPE_DARR)
+
+/**
+ * Return a pointer to the (I)th element of array `A`, making sure there is
+ * room for the element.
+ *
+ * If the array length is less than `I + 1` then the length is set to `I + 1`.
+ *
+ * The value `A` may be changed as a result of this call in which case any
+ * pointers into the previous memory block are no longer valid. The `A` value
+ * is guaranteed not to change if there is sufficient capacity in the array.
+ *
+ * Args:
+ *
+ * A: (IN/OUT) the dynamic array, can be NULL.
+ * I: the index to guarantee memory exists for
+ *
+ * Return:
+ * A pointer to the (I)th element in `A`
+ */
+#define darr_ensure_i_mt(A, I, MT) \
+ ({ \
+ assert((int)(I) >= 0 && (uint)(I) <= INT_MAX); \
+ int _i = (int)(I); \
+ if (_i > darr_maxi(A)) \
+ _darr_resize_mt((A), _i + 1, MT); \
+ assert((A) != NULL); \
+ if ((uint)_i + 1 > _darr_len(A)) \
+ _darr_len(A) = _i + 1; \
+ &(A)[_i]; \
+ })
+#define darr_ensure_i(A, I) darr_ensure_i_mt(A, I, MTYPE_DARR)
+
+#define _darr_insert_n(A, I, N, Z, MT) \
+ ({ \
+ (A) = __darr_insert_n(A, I, N, _darr_esize(A), Z, MT); \
+ &(A)[I]; \
+ })
+/**
+ * Insert N uninitialized elements in the array at index `I`.
+ *
+ * Previous elements from `I` are shifted right by `N`. Array length is
+ * increased by `N`.
+ *
+ * The value `A` may be changed as a result of this call in which case any
+ * pointers into the previous memory block are no longer valid. The `A` value
+ * is guaranteed not to change if there is sufficient capacity in the array.
+ *
+ * The `z` variant zeros new elements.
+ *
+ * Args:
+ * A: The dynamic array, can be NULL.
+ *
+ * Return:
+ * A pointer to the first inserted element in the array.
+ */
+#define darr_insert_n(A, I, N) _darr_insert_n(A, I, N, false, MTYPE_DARR)
+#define darr_insert_n_mt(A, I, N) _darr_insert_n(A, I, N, false, MT)
+#define darr_insert_nz(A, I, N) _darr_insert_n(A, I, N, true, MTYPE_DARR)
+#define darr_insert_nz_mt(A, I, N) _darr_insert_n(A, I, N, true, MT)
+
+/**
+ * Insert an uninitialized element in the array at index `I`.
+ *
+ * Previous elements from `I` are shifted right by 1. Array length is
+ * increased by 1.
+ *
+ * The value `A` may be changed as a result of this call in which case any
+ * pointers into the previous memory block are no longer valid. The `A` value
+ * is guaranteed not to change if there is sufficient capacity in the array.
+ *
+ * The `z` variant zeros the new element.
+ *
+ * Args:
+ * A: The dynamic array, can be NULL.
+ *
+ * Return:
+ * A pointer to the element in the array.
+ */
+#define darr_insert(A, I) _darr_insert_n(A, I, 1, false, MTYPE_DARR)
+#define darr_insert_mt(A, I) _darr_insert_n(A, I, 1, false, MT)
+#define darr_insertz(A, I) _darr_insert_n(A, I, 1, true, MTYPE_DARR)
+#define darr_insertz_mt(A, I) _darr_insert_n(A, I, 1, true, MT)
+
+/**
+ * Remove `N` elements from the array starting at index `I`.
+ *
+ * Elements from `I` + `N` are shifted left by `N`. Array length is reduced by
+ * `N`.
+ *
+ * Args:
+ * A: The dynamic array, can be NULL.
+ */
+#define darr_remove_n(A, I, N) \
+ do { \
+ uint __i = (I); \
+ uint __n = (N); \
+ uint __len = darr_len(A); \
+ if (!__len) \
+ break; \
+ else if (__i + __n < __len) { \
+ memmove(&(A)[__i], &(A)[__i + __n], \
+ _darr_esize(A) * (__len - (__i + __n))); \
+ _darr_len(A) = __len - __n; \
+ } else \
+ _darr_len(A) = __i; \
+ } while (0)
+
+/**
+ * Remove the `I`th element from the array.
+ *
+ * Previous elements from `I` + 1 are shifted left by 1, Array length is reduced
+ * by 1.
+ *
+ * Args:
+ * A: The dynamic array, can be NULL.
+ */
+#define darr_remove(A, I) darr_remove_n(A, I, 1)
+
+
+#define _darr_append_n(A, N, Z, MT) \
+ ({ \
+ uint __len = darr_len(A); \
+ darr_ensure_cap_mt(A, __len + (N), MT); \
+ _darr_len(A) = __len + (N); \
+ if (Z) \
+ memset(&(A)[__len], 0, (N)*_darr_esize(A)); \
+ &(A)[__len]; \
+ })
+/**
+ * Extending the array's length by N.
+ *
+ * Args:
+ * A: The dynamic array, can be NULL.
+ *
+ * The `z` variant zeros new elements.
+ *
+ * Return:
+ * A pointer to the first of the added elements at the end of the array.
+ */
+#define darr_append_n(A, N) _darr_append_n(A, N, false, MTYPE_DARR)
+#define darr_append_n_mt(A, N, MT) _darr_append_n(A, N, false, MT)
+#define darr_append_nz(A, N) _darr_append_n(A, N, true, MTYPE_DARR)
+#define darr_append_nz_mt(A, N, MT) _darr_append_n(A, N, true, MT)
+
+/**
+ * Extending the array's length by 1.
+ *
+ * Args:
+ * A: The dynamic array, can be NULL.
+ *
+ * The `z` variant zeros the new element.
+ *
+ * Return:
+ * A pointer to the new element at the end of the array.
+ */
+#define darr_append(A) _darr_append_n(A, 1, false, MTYPE_DARR)
+#define darr_append_mt(A, MT) _darr_append_n(A, 1, false, MT)
+#define darr_appendz(A) _darr_append_n(A, 1, true, MTYPE_DARR)
+#define darr_appendz_mt(A, MT) _darr_append_n(A, 1, true, MT)
+
+/**
+ * Append an element `E` onto the array `A`, extending it's length by 1.
+ *
+ * The `z` variant zeros the new element.
+ *
+ * Args:
+ * A: The dynamic array, can be NULL.
+ *
+ * Return:
+ * A pointer to the element in the array.
+ */
+#define darr_push(A, E) (*darr_append(A) = (E))
+#define darr_push_mt(A, E, MT) (*darr_append_mt(A, MT) = (E))
+#define darr_pushz(A) (darr_appendz(A))
+#define darr_pushz_mt(A, MT) (darr_appendz_mt(A, MT))
+
+
+/**
+ * Pop the last `N` elements from the array decrementing the length by `N`.
+ *
+ * Args:
+ * A: The dynamic array, can be NULL.
+ */
+#define darr_pop_n(A, N) \
+ do { \
+ if ((A) && (N) >= _darr_len(A)) \
+ darr_reset(A); \
+ else \
+ _darr_len(A) -= (N); \
+ } while (0)
+
+
+/**
+ * Pop the last element from the array decrementing the length by 1.
+ *
+ * Args:
+ * A: The dynamic array, can be NULL.
+ *
+ * Return:
+ * The element just popped.
+ */
+#define darr_pop(A) \
+ ({ \
+ uint __len = _darr_len(A); \
+ assert(__len); \
+ darr_remove(A, __len - 1); \
+ /* count on fact that we don't resize */ \
+ (A)[__len - 1]; \
+ })
+
+/**
+ * Return the address at the end of the array -- useful for iterating
+ *
+ * Args:
+ * A: The dynamic array, can be NULL.
+ *
+ * Return:
+ * The address of the end of the array (past the last elment) or NULL
+ * if `A` is NULL.
+ */
+#define darr_end(A) ((A) + darr_len(A))
+
+/**
+ * darr_last() - Get a pointer to the last element of the array.
+ * darr_strnul() - Get a pointer to the NUL byte of the darr string or NULL.
+ *
+ * Args:
+ * A: The dynamic array, can be NULL.
+ *
+ * Return:
+ * A pointer to the last element of the array or NULL if the array is
+ * empty.
+ */
+#define darr_last(A) \
+ ({ \
+ uint __len = darr_len(A); \
+ ((__len > 0) ? &(A)[__len - 1] : NULL); \
+ })
+#define darr_strnul(S) darr_last(S)
+
+/**
+ * darr_in_sprintf() - sprintf into D.
+ *
+ * Args:
+ * D: The destination darr, D's value may be NULL.
+ * F: The format string
+ * ...: variable arguments for format string.
+ *
+ * Return:
+ * The dynamic_array D with the new string content.
+ */
+#define darr_in_sprintf(D, F, ...) __darr_in_sprintf(&(D), 0, F, __VA_ARGS__)
+
+
+/**
+ * darr_in_strcat() - concat a string into a darr string.
+ *
+ * Args:
+ * D: The destination darr, D's value may be NULL.
+ * S: The string to concat onto D.
+ *
+ * Return:
+ * The dynamic_array D with the new string content.
+ */
+#define darr_in_strcat(D, S) \
+ ({ \
+ uint __dlen = darr_strlen(D); \
+ uint __slen = strlen(S); \
+ darr_ensure_cap_mt(D, __dlen + __slen + 1, MTYPE_DARR_STR); \
+ if (darr_len(D) == 0) \
+ *darr_append(D) = 0; \
+ memcpy(darr_last(D), (S), __slen + 1); \
+ _darr_len(D) += __slen; \
+ D; \
+ })
+
+/**
+ * darr_in_strcatf() - concat a formatted string into a darr string.
+ *
+ * Args:
+ * D: The destination darr, D's value may be NULL.
+ * F: The format string to concat onto D after adding arguments.
+ * ...: The arguments for the format string.
+ * Return:
+ * The dynamic_array D with the new string content.
+ */
+#define darr_in_strcatf(D, F, ...) \
+ __darr_in_sprintf(&(D), true, (F), __VA_ARGS__)
+
+/**
+ * darr_in_strcat_tail() - copies end of one darr str to another.
+ *
+ * This is a rather specialized function, it takes 2 darr's, a destination and a
+ * source. If the source is not longer than the destination nothing is done.
+ * Otherwise the characters in the source that lie beyond the length of the dest
+ * are added to the dest. No checking is done to make sure the common prefix
+ * matches. For example:
+ *
+ * D: "/foo"
+ * S: "/foo/bar"
+ * -> D: "/foo/bar"
+ *
+ * perhaps surprising results:
+ * D: "/foo"
+ * S: "/zoo/bar"
+ * -> D: "/foo/bar"
+ *
+ * Args:
+ * D: The destination darr, D's value may be NULL.
+ * S: The string to copy the tail from.
+ *
+ * Return:
+ * The dynamic_array D with the extended string content.
+ */
+#define darr_in_strcat_tail(D, S) \
+ ({ \
+ int __dsize, __ssize, __extra; \
+ \
+ if (darr_len(D) == 0) \
+ *darr_append(D) = 0; \
+ __dsize = darr_ilen(D); \
+ __ssize = darr_ilen(S); \
+ __extra = __ssize - __dsize; \
+ if (__extra > 0) { \
+ darr_ensure_cap_mt(D, (uint)__ssize, MTYPE_DARR_STR); \
+ memcpy(darr_last(D), (S) + __dsize - 1, __extra + 1); \
+ _darr_len(D) += __extra; \
+ } \
+ D; \
+ })
+
+/**
+ * darr_in_strdup_cap() - duplicate the string into a darr reserving capacity.
+ * darr_in_strdup() - duplicate the string into a darr.
+ *
+ * Args:
+ * D: The destination darr, D's value may be NULL.
+ * S: The string to duplicate.
+ * C: The capacity to reserve.
+ *
+ * Return:
+ * The dynamic_array D with the duplicated string.
+ */
+#define darr_in_strdup_cap(D, S, C) \
+ ({ \
+ size_t __size = strlen(S) + 1; \
+ darr_reset(D); \
+ darr_ensure_cap_mt(D, \
+ ((size_t)(C) > __size) ? (size_t)(C) \
+ : __size, \
+ MTYPE_DARR_STR); \
+ strlcpy(D, (S), darr_cap(D)); \
+ darr_setlen((D), (size_t)__size); \
+ D; \
+ })
+#define darr_in_strdup(D, S) darr_in_strdup_cap(D, S, 1)
+
+/**
+ * darr_in_vsprintf() - vsprintf into D.
+ *
+ * Args:
+ * D: The destination darr, D's value may be NULL.
+ * F: The format string
+ * A: Varargs
+ *
+ * Return:
+ * The dynamic_array D with the new string content.
+ */
+#define darr_in_vsprintf(D, F, A) __darr_in_vsprintf(&(D), 0, F, A)
+
+/**
+ * darr_in_vstrcatf() - concat a formatted string into a darr string.
+ *
+ * Args:
+ * D: The destination darr, D's value may be NULL.
+ * F: The format string to concat onto D after adding arguments.
+ * A: Varargs
+ *
+ * Return:
+ * The dynamic_array D with the new string content.
+ */
+#define darr_in_vstrcatf(D, F, A) __darr_in_vsprintf(&(D), true, (F), (A))
+
+/**
+ * darr_sprintf() - sprintf into a new dynamic array.
+ *
+ * Args:
+ * F: The format string
+ * ...: variable arguments for format string.
+ *
+ * Return:
+ * A char * dynamic_array with the new string content.
+ */
+#define darr_sprintf(F, ...) \
+ ({ \
+ char *d = NULL; \
+ __darr_in_sprintf(&d, false, F, __VA_ARGS__); \
+ d; \
+ })
+
+/**
+ * darr_strdup_cap() - duplicate the string reserving capacity.
+ * darr_strdup() - duplicate the string into a dynamic array.
+ *
+ * Args:
+ * S: The string to duplicate.
+ * C: The capacity to reserve.
+ *
+ * Return:
+ * The dynamic_array with the duplicated string.
+ */
+#define darr_strdup_cap(S, C) \
+ ({ \
+ size_t __size = strlen(S) + 1; \
+ char *__s = NULL; \
+ /* Cast to ssize_t to avoid warning when C == 0 */ \
+ darr_ensure_cap_mt(__s, \
+ ((ssize_t)(C) > (ssize_t)__size) \
+ ? (size_t)(C) \
+ : __size, \
+ MTYPE_DARR_STR); \
+ strlcpy(__s, (S), darr_cap(__s)); \
+ darr_setlen(__s, (size_t)__size); \
+ __s; \
+ })
+#define darr_strdup(S) darr_strdup_cap(S, 0)
+
+/**
+ * darr_strlen() - get the length of the NUL terminated string in a darr.
+ *
+ * Args:
+ * S: The string to measure, value may be NULL.
+ *
+ * Return:
+ * The length of the NUL terminated string in @S
+ */
+#define darr_strlen(S) \
+ ({ \
+ uint __size = darr_len(S); \
+ if (__size) \
+ __size -= 1; \
+ assert(!(S) || ((char *)(S))[__size] == 0); \
+ __size; \
+ })
+
+/**
+ * darr_vsprintf() - vsprintf into a new dynamic array.
+ *
+ * Args:
+ * F: The format string
+ * A: Varargs
+ *
+ * Return:
+ * The dynamic_array D with the new string content.
+ */
+#define darr_vsprintf(F, A) \
+ ({ \
+ char *d = NULL; \
+ darr_in_vsprintf(d, F, A); \
+ d; \
+ })
+
+/**
+ * Iterate over array `A` using a pointer to each element in `P`.
+ *
+ * Args:
+ * A: The dynamic array, can be NULL.
+ * P: A variable with the same type as A used as the iterator.
+ */
+#define darr_foreach_p(A, P) for ((P) = (A); (P) < darr_end(A); (P)++)
+
+/**
+ * Iterate over array `A`s indices.
+ *
+ * Args:
+ * A: The dynamic array, can be NULL.
+ * I: A uint variable to store the current element index in.
+ */
+#define darr_foreach_i(A, I) for ((I) = 0; (I) < darr_len(A); (I)++)
+
+#endif /* _FRR_DARR_H_ */
diff --git a/lib/defun_lex.l b/lib/defun_lex.l
index 124f864166..3104e48063 100644
--- a/lib/defun_lex.l
+++ b/lib/defun_lex.l
@@ -140,6 +140,7 @@ SPECIAL [(),]
"DEFPY_ATTR" value = strdup(yytext); return DEFUNNY;
"DEFPY_HIDDEN" value = strdup(yytext); return DEFUNNY;
"DEFPY_YANG" value = strdup(yytext); return DEFUNNY;
+"DEFPY_YANG_HIDDEN" value = strdup(yytext); return DEFUNNY;
"DEFPY_YANG_NOSH" value = strdup(yytext); return DEFUNNY;
"ALIAS" value = strdup(yytext); return DEFUNNY;
"ALIAS_HIDDEN" value = strdup(yytext); return DEFUNNY;
diff --git a/lib/distribute.c b/lib/distribute.c
index 65487676d6..ccd1f1379e 100644
--- a/lib/distribute.c
+++ b/lib/distribute.c
@@ -17,8 +17,6 @@ DEFINE_MTYPE_STATIC(LIB, DISTRIBUTE, "Distribute list");
DEFINE_MTYPE_STATIC(LIB, DISTRIBUTE_IFNAME, "Dist-list ifname");
DEFINE_MTYPE_STATIC(LIB, DISTRIBUTE_NAME, "Dist-list name");
-static struct list *dist_ctx_list;
-
static struct distribute *distribute_new(void)
{
return XCALLOC(MTYPE_DISTRIBUTE, sizeof(struct distribute));
@@ -244,11 +242,10 @@ static enum distribute_type distribute_direction(const char *dir, bool v4)
__builtin_unreachable();
}
-int distribute_list_parser(bool prefix, bool v4, const char *dir,
- const char *list, const char *ifname)
+int distribute_list_parser(struct distribute_ctx *ctx, bool prefix, bool v4,
+ const char *dir, const char *list, const char *ifname)
{
enum distribute_type type = distribute_direction(dir, v4);
- struct distribute_ctx *ctx = listnode_head(dist_ctx_list);
void (*distfn)(struct distribute_ctx *, const char *,
enum distribute_type, const char *) =
@@ -259,12 +256,12 @@ int distribute_list_parser(bool prefix, bool v4, const char *dir,
return CMD_SUCCESS;
}
-int distribute_list_no_parser(struct vty *vty, bool prefix, bool v4,
- const char *dir, const char *list,
- const char *ifname)
+
+int distribute_list_no_parser(struct distribute_ctx *ctx, struct vty *vty,
+ bool prefix, bool v4, const char *dir,
+ const char *list, const char *ifname)
{
enum distribute_type type = distribute_direction(dir, v4);
- struct distribute_ctx *ctx = listnode_head(dist_ctx_list);
int ret;
int (*distfn)(struct distribute_ctx *, const char *,
@@ -274,7 +271,8 @@ int distribute_list_no_parser(struct vty *vty, bool prefix, bool v4,
ret = distfn(ctx, ifname, type, list);
if (!ret) {
- vty_out(vty, "distribute list doesn't exist\n");
+ if (vty)
+ vty_out(vty, "distribute list doesn't exist\n");
return CMD_WARNING_CONFIG_FAILED;
}
@@ -443,17 +441,130 @@ int config_write_distribute(struct vty *vty,
return write;
}
+/* ---------- */
+/* Northbound */
+/* ---------- */
+
+int group_distribute_list_create_helper(
+ struct nb_cb_create_args *args, struct distribute_ctx *ctx)
+{
+ nb_running_set_entry(args->dnode, ctx);
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/distribute-lists/distribute-list/{in,out}/{access,prefix}-list
+ */
+
+int group_distribute_list_destroy(struct nb_cb_destroy_args *args)
+{
+ nb_running_unset_entry(args->dnode);
+ return NB_OK;
+}
+
+static int distribute_list_leaf_update(const struct lyd_node *dnode,
+ int ip_version, bool no)
+{
+ struct distribute_ctx *ctx;
+ struct lyd_node *dir_node = lyd_parent(dnode);
+ struct lyd_node_inner *list_node = dir_node->parent;
+ struct lyd_node *intf_key = list_node->child;
+ bool ipv4 = ip_version == 4 ? true : false;
+ bool prefix;
+
+ ctx = nb_running_get_entry_non_rec(&list_node->node, NULL, false);
+
+ prefix = dnode->schema->name[0] == 'p' ? true : false;
+ if (no)
+ distribute_list_no_parser(ctx, NULL, prefix, ipv4,
+ dir_node->schema->name,
+ lyd_get_value(dnode),
+ lyd_get_value(intf_key));
+ else
+ distribute_list_parser(ctx, prefix, ipv4,
+ dir_node->schema->name,
+ lyd_get_value(dnode),
+ lyd_get_value(intf_key));
+ return NB_OK;
+}
+
+static int distribute_list_leaf_modify(struct nb_cb_modify_args *args,
+ int ip_version)
+{
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+ return distribute_list_leaf_update(args->dnode, ip_version, false);
+}
+
+static int distribute_list_leaf_destroy(struct nb_cb_destroy_args *args,
+ int ip_version)
+{
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+ return distribute_list_leaf_update(args->dnode, ip_version, true);
+}
+
+int group_distribute_list_ipv4_modify(struct nb_cb_modify_args *args)
+{
+ return distribute_list_leaf_modify(args, 4);
+}
+int group_distribute_list_ipv4_destroy(struct nb_cb_destroy_args *args)
+{
+ return distribute_list_leaf_destroy(args, 4);
+}
+int group_distribute_list_ipv6_modify(struct nb_cb_modify_args *args)
+{
+ return distribute_list_leaf_modify(args, 6);
+}
+int group_distribute_list_ipv6_destroy(struct nb_cb_destroy_args *args)
+{
+ return distribute_list_leaf_destroy(args, 6);
+}
+
+static int distribute_list_leaf_cli_show(struct vty *vty,
+ const struct lyd_node *dnode,
+ int ip_version)
+{
+ struct lyd_node *dir_node = lyd_parent(dnode);
+ struct lyd_node_inner *list_node = dir_node->parent;
+ struct lyd_node *intf_key = list_node->child;
+ bool ipv6 = ip_version == 6 ? true : false;
+ bool prefix;
+
+ prefix = dnode->schema->name[0] == 'p' ? true : false;
+ vty_out(vty,
+ " %sdistribute-list %s%s %s %s\n",
+ ipv6 ? "ipv6 " : "",
+ prefix ? "prefix " : "",
+ lyd_get_value(dnode),
+ dir_node->schema->name,
+ lyd_get_value(intf_key));
+
+ return NB_OK;
+}
+
+void group_distribute_list_ipv4_cli_show(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ distribute_list_leaf_cli_show(vty, dnode, 4);
+}
+void group_distribute_list_ipv6_cli_show(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ distribute_list_leaf_cli_show(vty, dnode, 6);
+}
+
+/* ------------- */
+/* Setup/Cleanup */
+/* ------------- */
+
void distribute_list_delete(struct distribute_ctx **ctx)
{
hash_clean_and_free(&(*ctx)->disthash,
(void (*)(void *))distribute_free);
- if (dist_ctx_list) {
- listnode_delete(dist_ctx_list, *ctx);
- if (list_isempty(dist_ctx_list))
- list_delete(&dist_ctx_list);
- }
-
XFREE(MTYPE_DISTRIBUTE_CTX, (*ctx));
}
@@ -464,11 +575,9 @@ struct distribute_ctx *distribute_list_ctx_create(struct vrf *vrf)
ctx = XCALLOC(MTYPE_DISTRIBUTE_CTX, sizeof(struct distribute_ctx));
ctx->vrf = vrf;
- ctx->disthash = hash_create(
- distribute_hash_make,
- (bool (*)(const void *, const void *))distribute_cmp, NULL);
- if (!dist_ctx_list)
- dist_ctx_list = list_new();
- listnode_add(dist_ctx_list, ctx);
+ ctx->disthash =
+ hash_create(distribute_hash_make,
+ (bool (*)(const void *, const void *))distribute_cmp,
+ NULL);
return ctx;
}
diff --git a/lib/distribute.h b/lib/distribute.h
index 75783712a1..a0bc348982 100644
--- a/lib/distribute.h
+++ b/lib/distribute.h
@@ -9,6 +9,7 @@
#include <zebra.h>
#include "if.h"
#include "filter.h"
+#include "northbound.h"
#ifdef __cplusplus
extern "C" {
@@ -69,11 +70,43 @@ extern enum filter_type distribute_apply_in(struct interface *,
extern enum filter_type distribute_apply_out(struct interface *,
struct prefix *);
-extern int distribute_list_parser(bool prefix, bool v4, const char *dir,
- const char *list, const char *ifname);
-extern int distribute_list_no_parser(struct vty *vty, bool prefix, bool v4,
+extern int distribute_list_parser(struct distribute_ctx *ctx, bool prefix,
+ bool v4, const char *dir, const char *list,
+ const char *ifname);
+extern int distribute_list_no_parser(struct distribute_ctx *ctx,
+ struct vty *vty, bool prefix, bool v4,
const char *dir, const char *list,
const char *ifname);
+
+/*
+ * Northbound
+ */
+
+/*
+ * Define your own create callback and then call thes helper with your
+ * distribute list context when a list entry is created. Additionally, plug the
+ * destroy callback into the frr_module_yang_info struct, or call it if you have
+ * your own callback destroy function.
+ */
+extern int group_distribute_list_create_helper(struct nb_cb_create_args *args,
+ struct distribute_ctx *ctx);
+extern int group_distribute_list_destroy(struct nb_cb_destroy_args *args);
+
+/*
+ * Plug 3 of these handlers in for your distribute-list for all the northbound
+ * distribute_list leaf callbacks. If you need multi-protocol then use the
+ * grouping twice under 2 different containers.
+ */
+extern int group_distribute_list_ipv4_modify(struct nb_cb_modify_args *args);
+extern int group_distribute_list_ipv4_destroy(struct nb_cb_destroy_args *args);
+extern void group_distribute_list_ipv4_cli_show(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+extern int group_distribute_list_ipv6_modify(struct nb_cb_modify_args *args);
+extern int group_distribute_list_ipv6_destroy(struct nb_cb_destroy_args *args);
+extern void group_distribute_list_ipv6_cli_show(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
#ifdef __cplusplus
}
#endif
diff --git a/lib/elf_py.c b/lib/elf_py.c
index d473dc10cb..643495d8c7 100644
--- a/lib/elf_py.c
+++ b/lib/elf_py.c
@@ -1089,7 +1089,9 @@ static void elffile_add_dynreloc(struct elffile *w, Elf_Data *reldata,
symidx = relw->symidx = GELF_R_SYM(rela->r_info);
sym = relw->sym = gelf_getsym(symdata, symidx, &relw->_sym);
if (sym) {
- relw->symname = elfdata_strptr(strdata, sym->st_name);
+ if (strdata)
+ relw->symname = elfdata_strptr(strdata,
+ sym->st_name);
relw->symvalid = GELF_ST_TYPE(sym->st_info)
!= STT_NOTYPE;
relw->unresolved = sym->st_shndx == SHN_UNDEF;
@@ -1140,7 +1142,7 @@ static PyObject *elffile_load(PyTypeObject *type, PyObject *args,
fd = open(filename, O_RDONLY | O_NOCTTY);
if (fd < 0 || fstat(fd, &st)) {
PyErr_SetFromErrnoWithFilename(PyExc_OSError, filename);
- if (fd > 0)
+ if (fd >= 0)
close(fd);
goto out;
}
diff --git a/lib/event.c b/lib/event.c
index a8eb89f48d..fc46a11c0b 100644
--- a/lib/event.c
+++ b/lib/event.c
@@ -6,6 +6,8 @@
/* #define DEBUG */
#include <zebra.h>
+
+#include <signal.h>
#include <sys/resource.h>
#include "frrevent.h"
@@ -55,11 +57,6 @@ static int event_timer_cmp(const struct event *a, const struct event *b)
DECLARE_HEAP(event_timer_list, struct event, timeritem, event_timer_cmp);
-#if defined(__APPLE__)
-#include <mach/mach.h>
-#include <mach/mach_time.h>
-#endif
-
#define AWAKEN(m) \
do { \
const unsigned char wakebyte = 0x01; \
@@ -75,48 +72,48 @@ static struct list *masters;
static void thread_free(struct event_loop *master, struct event *thread);
-#ifndef EXCLUDE_CPU_TIME
-#define EXCLUDE_CPU_TIME 0
-#endif
-#ifndef CONSUMED_TIME_CHECK
-#define CONSUMED_TIME_CHECK 0
-#endif
-
-bool cputime_enabled = !EXCLUDE_CPU_TIME;
+bool cputime_enabled = true;
unsigned long cputime_threshold = CONSUMED_TIME_CHECK;
unsigned long walltime_threshold = CONSUMED_TIME_CHECK;
/* CLI start ---------------------------------------------------------------- */
#include "lib/event_clippy.c"
-static unsigned int cpu_record_hash_key(const struct cpu_event_history *a)
+static uint32_t cpu_record_hash_key(const struct cpu_event_history *a)
{
int size = sizeof(a->func);
return jhash(&a->func, size, 0);
}
-static bool cpu_record_hash_cmp(const struct cpu_event_history *a,
- const struct cpu_event_history *b)
+static int cpu_record_hash_cmp(const struct cpu_event_history *a,
+ const struct cpu_event_history *b)
{
- return a->func == b->func;
+ return numcmp((uintptr_t)a->func, (uintptr_t)b->func);
}
-static void *cpu_record_hash_alloc(struct cpu_event_history *a)
+DECLARE_HASH(cpu_records, struct cpu_event_history, item, cpu_record_hash_cmp,
+ cpu_record_hash_key);
+
+static struct cpu_event_history *cpu_records_get(struct event_loop *loop,
+ void (*func)(struct event *e),
+ const char *funcname)
{
- struct cpu_event_history *new;
+ struct cpu_event_history ref = { .func = func }, *res;
- new = XCALLOC(MTYPE_EVENT_STATS, sizeof(struct cpu_event_history));
- new->func = a->func;
- new->funcname = a->funcname;
- return new;
+ res = cpu_records_find(loop->cpu_records, &ref);
+ if (!res) {
+ res = XCALLOC(MTYPE_EVENT_STATS, sizeof(*res));
+ res->func = func;
+ res->funcname = funcname;
+ cpu_records_add(loop->cpu_records, res);
+ }
+ return res;
}
-static void cpu_record_hash_free(void *a)
+static void cpu_records_free(struct cpu_event_history **p)
{
- struct cpu_event_history *hist = a;
-
- XFREE(MTYPE_EVENT_STATS, hist);
+ XFREE(MTYPE_EVENT_STATS, *p);
}
static void vty_out_cpu_event_history(struct vty *vty,
@@ -136,14 +133,11 @@ static void vty_out_cpu_event_history(struct vty *vty,
a->types & (1 << EVENT_EXECUTE) ? 'X' : ' ', a->funcname);
}
-static void cpu_record_hash_print(struct hash_bucket *bucket, void *args[])
+static void cpu_record_print_one(struct vty *vty, uint8_t filter,
+ struct cpu_event_history *totals,
+ const struct cpu_event_history *a)
{
- struct cpu_event_history *totals = args[0];
struct cpu_event_history copy;
- struct vty *vty = args[1];
- uint8_t *filter = args[2];
-
- struct cpu_event_history *a = bucket->data;
copy.total_active =
atomic_load_explicit(&a->total_active, memory_order_seq_cst);
@@ -165,7 +159,7 @@ static void cpu_record_hash_print(struct hash_bucket *bucket, void *args[])
copy.types = atomic_load_explicit(&a->types, memory_order_seq_cst);
copy.funcname = a->funcname;
- if (!(copy.types & *filter))
+ if (!(copy.types & filter))
return;
vty_out_cpu_event_history(vty, &copy);
@@ -185,7 +179,6 @@ static void cpu_record_hash_print(struct hash_bucket *bucket, void *args[])
static void cpu_record_print(struct vty *vty, uint8_t filter)
{
struct cpu_event_history tmp;
- void *args[3] = {&tmp, vty, &filter};
struct event_loop *m;
struct listnode *ln;
@@ -220,15 +213,15 @@ static void cpu_record_print(struct vty *vty, uint8_t filter)
"Active Runtime(ms) Invoked Avg uSec Max uSecs");
vty_out(vty, " Avg uSec Max uSecs");
vty_out(vty,
- " CPU_Warn Wall_Warn Starv_Warn Type Thread\n");
-
- if (m->cpu_record->count)
- hash_iterate(
- m->cpu_record,
- (void (*)(struct hash_bucket *,
- void *))cpu_record_hash_print,
- args);
- else
+ " CPU_Warn Wall_Warn Starv_Warn Type Event\n");
+
+ if (cpu_records_count(m->cpu_records)) {
+ struct cpu_event_history *rec;
+
+ frr_each (cpu_records, m->cpu_records, rec)
+ cpu_record_print_one(vty, filter, &tmp,
+ rec);
+ } else
vty_out(vty, "No data to display yet.\n");
vty_out(vty, "\n");
@@ -236,47 +229,41 @@ static void cpu_record_print(struct vty *vty, uint8_t filter)
}
vty_out(vty, "\n");
- vty_out(vty, "Total thread statistics\n");
+ vty_out(vty, "Total Event statistics\n");
vty_out(vty, "-------------------------\n");
vty_out(vty, "%30s %18s %18s\n", "",
"CPU (user+system):", "Real (wall-clock):");
vty_out(vty, "Active Runtime(ms) Invoked Avg uSec Max uSecs");
- vty_out(vty, " Avg uSec Max uSecs CPU_Warn Wall_Warn");
- vty_out(vty, " Type Thread\n");
+ vty_out(vty, " Avg uSec Max uSecs CPU_Warn Wall_Warn Starv_Warn");
+ vty_out(vty, " Type Event\n");
if (tmp.total_calls > 0)
vty_out_cpu_event_history(vty, &tmp);
}
-static void cpu_record_hash_clear(struct hash_bucket *bucket, void *args[])
-{
- uint8_t *filter = args[0];
- struct hash *cpu_record = args[1];
-
- struct cpu_event_history *a = bucket->data;
-
- if (!(a->types & *filter))
- return;
-
- hash_release(cpu_record, bucket->data);
-}
-
static void cpu_record_clear(uint8_t filter)
{
- uint8_t *tmp = &filter;
struct event_loop *m;
struct listnode *ln;
frr_with_mutex (&masters_mtx) {
for (ALL_LIST_ELEMENTS_RO(masters, ln, m)) {
frr_with_mutex (&m->mtx) {
- void *args[2] = {tmp, m->cpu_record};
+ struct cpu_event_history *item;
+ struct cpu_records_head old[1];
+
+ cpu_records_init(old);
+ cpu_records_swap_all(old, m->cpu_records);
+
+ while ((item = cpu_records_pop(old))) {
+ if (item->types & filter)
+ cpu_records_free(&item);
+ else
+ cpu_records_add(m->cpu_records,
+ item);
+ }
- hash_iterate(
- m->cpu_record,
- (void (*)(struct hash_bucket *,
- void *))cpu_record_hash_clear,
- args);
+ cpu_records_fini(old);
}
}
}
@@ -317,13 +304,16 @@ static uint8_t parse_filter(const char *filterstr)
return filter;
}
-DEFUN_NOSH (show_thread_cpu,
- show_thread_cpu_cmd,
- "show thread cpu [FILTER]",
- SHOW_STR
- "Thread information\n"
- "Thread CPU usage\n"
- "Display filter (rwtex)\n")
+#if CONFDATE > 20240707
+ CPP_NOTICE("Remove `show thread ...` commands")
+#endif
+DEFUN_NOSH (show_event_cpu,
+ show_event_cpu_cmd,
+ "show event cpu [FILTER]",
+ SHOW_STR
+ "Event information\n"
+ "Event CPU usage\n"
+ "Display filter (rwtexb)\n")
{
uint8_t filter = (uint8_t)-1U;
int idx = 0;
@@ -342,6 +332,14 @@ DEFUN_NOSH (show_thread_cpu,
return CMD_SUCCESS;
}
+ALIAS(show_event_cpu,
+ show_thread_cpu_cmd,
+ "show thread cpu [FILTER]",
+ SHOW_STR
+ "Thread information\n"
+ "Thread CPU usage\n"
+ "Display filter (rwtex)\n")
+
DEFPY (service_cputime_stats,
service_cputime_stats_cmd,
"[no] service cputime-stats",
@@ -355,7 +353,7 @@ DEFPY (service_cputime_stats,
DEFPY (service_cputime_warning,
service_cputime_warning_cmd,
- "[no] service cputime-warning (1-4294967295)",
+ "[no] service cputime-warning ![(1-4294967295)]",
NO_STR
"Set up miscellaneous service\n"
"Warn for tasks exceeding CPU usage threshold\n"
@@ -368,16 +366,9 @@ DEFPY (service_cputime_warning,
return CMD_SUCCESS;
}
-ALIAS (service_cputime_warning,
- no_service_cputime_warning_cmd,
- "no service cputime-warning",
- NO_STR
- "Set up miscellaneous service\n"
- "Warn for tasks exceeding CPU usage threshold\n")
-
DEFPY (service_walltime_warning,
service_walltime_warning_cmd,
- "[no] service walltime-warning (1-4294967295)",
+ "[no] service walltime-warning ![(1-4294967295)]",
NO_STR
"Set up miscellaneous service\n"
"Warn for tasks exceeding total wallclock threshold\n"
@@ -390,14 +381,7 @@ DEFPY (service_walltime_warning,
return CMD_SUCCESS;
}
-ALIAS (service_walltime_warning,
- no_service_walltime_warning_cmd,
- "no service walltime-warning",
- NO_STR
- "Set up miscellaneous service\n"
- "Warn for tasks exceeding total wallclock threshold\n")
-
-static void show_thread_poll_helper(struct vty *vty, struct event_loop *m)
+static void show_event_poll_helper(struct vty *vty, struct event_loop *m)
{
const char *name = m->name ? m->name : "main";
char underline[strlen(name) + 1];
@@ -438,24 +422,30 @@ static void show_thread_poll_helper(struct vty *vty, struct event_loop *m)
}
}
-DEFUN_NOSH (show_thread_poll,
- show_thread_poll_cmd,
- "show thread poll",
- SHOW_STR
- "Thread information\n"
- "Show poll FD's and information\n")
+DEFUN_NOSH (show_event_poll,
+ show_event_poll_cmd,
+ "show event poll",
+ SHOW_STR
+ "Event information\n"
+ "Event Poll Information\n")
{
struct listnode *node;
struct event_loop *m;
frr_with_mutex (&masters_mtx) {
for (ALL_LIST_ELEMENTS_RO(masters, node, m))
- show_thread_poll_helper(vty, m);
+ show_event_poll_helper(vty, m);
}
return CMD_SUCCESS;
}
+ALIAS(show_event_poll,
+ show_thread_poll_cmd,
+ "show thread poll",
+ SHOW_STR
+ "Thread information\n"
+ "Show poll FD's and information\n")
DEFUN (clear_thread_cpu,
clear_thread_cpu_cmd,
@@ -482,7 +472,7 @@ DEFUN (clear_thread_cpu,
return CMD_SUCCESS;
}
-static void show_thread_timers_helper(struct vty *vty, struct event_loop *m)
+static void show_event_timers_helper(struct vty *vty, struct event_loop *m)
{
const char *name = m->name ? m->name : "main";
char underline[strlen(name) + 1];
@@ -499,37 +489,45 @@ static void show_thread_timers_helper(struct vty *vty, struct event_loop *m)
}
}
-DEFPY_NOSH (show_thread_timers,
- show_thread_timers_cmd,
- "show thread timers",
- SHOW_STR
- "Thread information\n"
- "Show all timers and how long they have in the system\n")
+DEFPY_NOSH (show_event_timers,
+ show_event_timers_cmd,
+ "show event timers",
+ SHOW_STR
+ "Event information\n"
+ "Show all timers and how long they have in the system\n")
{
struct listnode *node;
struct event_loop *m;
frr_with_mutex (&masters_mtx) {
for (ALL_LIST_ELEMENTS_RO(masters, node, m))
- show_thread_timers_helper(vty, m);
+ show_event_timers_helper(vty, m);
}
return CMD_SUCCESS;
}
+ALIAS(show_event_timers,
+ show_thread_timers_cmd,
+ "show thread timers",
+ SHOW_STR
+ "Thread information\n"
+ "Show all timers and how long they have in the system\n")
+
void event_cmd_init(void)
{
install_element(VIEW_NODE, &show_thread_cpu_cmd);
+ install_element(VIEW_NODE, &show_event_cpu_cmd);
install_element(VIEW_NODE, &show_thread_poll_cmd);
+ install_element(VIEW_NODE, &show_event_poll_cmd);
install_element(ENABLE_NODE, &clear_thread_cpu_cmd);
install_element(CONFIG_NODE, &service_cputime_stats_cmd);
install_element(CONFIG_NODE, &service_cputime_warning_cmd);
- install_element(CONFIG_NODE, &no_service_cputime_warning_cmd);
install_element(CONFIG_NODE, &service_walltime_warning_cmd);
- install_element(CONFIG_NODE, &no_service_walltime_warning_cmd);
install_element(VIEW_NODE, &show_thread_timers_cmd);
+ install_element(VIEW_NODE, &show_event_timers_cmd);
}
/* CLI end ------------------------------------------------------------------ */
@@ -545,6 +543,7 @@ static void initializer(void)
pthread_key_create(&thread_current, NULL);
}
+#define STUPIDLY_LARGE_FD_SIZE 100000
struct event_loop *event_master_create(const char *name)
{
struct event_loop *rv;
@@ -571,6 +570,13 @@ struct event_loop *event_master_create(const char *name)
rv->fd_limit = (int)limit.rlim_cur;
}
+ if (rv->fd_limit > STUPIDLY_LARGE_FD_SIZE) {
+ zlog_warn("FD Limit set: %u is stupidly large. Is this what you intended? Consider using --limit-fds also limiting size to %u",
+ rv->fd_limit, STUPIDLY_LARGE_FD_SIZE);
+
+ rv->fd_limit = STUPIDLY_LARGE_FD_SIZE;
+ }
+
rv->read = XCALLOC(MTYPE_EVENT_POLL,
sizeof(struct event *) * rv->fd_limit);
@@ -581,10 +587,7 @@ struct event_loop *event_master_create(const char *name)
snprintf(tmhashname, sizeof(tmhashname), "%s - threadmaster event hash",
name);
- rv->cpu_record = hash_create_size(
- 8, (unsigned int (*)(const void *))cpu_record_hash_key,
- (bool (*)(const void *, const void *))cpu_record_hash_cmp,
- tmhashname);
+ cpu_records_init(rv->cpu_records);
event_list_init(&rv->event);
event_list_init(&rv->ready);
@@ -702,6 +705,7 @@ void event_master_free_unused(struct event_loop *m)
/* Stop thread scheduler. */
void event_master_free(struct event_loop *m)
{
+ struct cpu_event_history *record;
struct event *t;
frr_with_mutex (&masters_mtx) {
@@ -724,7 +728,9 @@ void event_master_free(struct event_loop *m)
list_delete(&m->cancel_req);
m->cancel_req = NULL;
- hash_clean_and_free(&m->cpu_record, cpu_record_hash_free);
+ while ((record = cpu_records_pop(m->cpu_records)))
+ cpu_records_free(&record);
+ cpu_records_fini(m->cpu_records);
XFREE(MTYPE_EVENT_MASTER, m->name);
XFREE(MTYPE_EVENT_MASTER, m->handler.pfds);
@@ -797,7 +803,6 @@ static struct event *thread_get(struct event_loop *m, uint8_t type,
const struct xref_eventsched *xref)
{
struct event *thread = event_list_pop(&m->unuse);
- struct cpu_event_history tmp;
if (!thread) {
thread = XCALLOC(MTYPE_THREAD, sizeof(struct event));
@@ -811,7 +816,12 @@ static struct event *thread_get(struct event_loop *m, uint8_t type,
thread->master = m;
thread->arg = arg;
thread->yield = EVENT_YIELD_TIME_SLOT; /* default */
- thread->ref = NULL;
+ /* thread->ref is zeroed either by XCALLOC above or by memset before
+ * being put on the "unuse" list by thread_add_unuse().
+ * Setting it here again makes coverity complain about a missing
+ * lock :(
+ */
+ /* thread->ref = NULL; */
thread->ignore_timer_late = false;
/*
@@ -825,13 +835,9 @@ static struct event *thread_get(struct event_loop *m, uint8_t type,
* hash_get lookups.
*/
if ((thread->xref && thread->xref->funcname != xref->funcname)
- || thread->func != func) {
- tmp.func = func;
- tmp.funcname = xref->funcname;
- thread->hist =
- hash_get(m->cpu_record, &tmp,
- (void *(*)(void *))cpu_record_hash_alloc);
- }
+ || thread->func != func)
+ thread->hist = cpu_records_get(m, func, xref->funcname);
+
thread->hist->total_active++;
thread->func = func;
thread->xref = xref;
@@ -1491,9 +1497,9 @@ void event_cancel(struct event **thread)
cr->thread = *thread;
listnode_add(master->cancel_req, cr);
do_event_cancel(master);
- }
- *thread = NULL;
+ *thread = NULL;
+ }
}
/**
@@ -1620,12 +1626,70 @@ static int thread_process_io_helper(struct event_loop *m, struct event *thread,
return 1;
}
+static inline void thread_process_io_inner_loop(struct event_loop *m,
+ unsigned int num,
+ struct pollfd *pfds, nfds_t *i,
+ uint32_t *ready)
+{
+ /* no event for current fd? immediately continue */
+ if (pfds[*i].revents == 0)
+ return;
+
+ *ready = *ready + 1;
+
+ /*
+ * Unless someone has called event_cancel from another
+ * pthread, the only thing that could have changed in
+ * m->handler.pfds while we were asleep is the .events
+ * field in a given pollfd. Barring event_cancel() that
+ * value should be a superset of the values we have in our
+ * copy, so there's no need to update it. Similarily,
+ * barring deletion, the fd should still be a valid index
+ * into the master's pfds.
+ *
+ * We are including POLLERR here to do a READ event
+ * this is because the read should fail and the
+ * read function should handle it appropriately
+ */
+ if (pfds[*i].revents & (POLLIN | POLLHUP | POLLERR)) {
+ thread_process_io_helper(m, m->read[pfds[*i].fd], POLLIN,
+ pfds[*i].revents, *i);
+ }
+ if (pfds[*i].revents & POLLOUT)
+ thread_process_io_helper(m, m->write[pfds[*i].fd], POLLOUT,
+ pfds[*i].revents, *i);
+
+ /*
+ * if one of our file descriptors is garbage, remove the same
+ * from both pfds + update sizes and index
+ */
+ if (pfds[*i].revents & POLLNVAL) {
+ memmove(m->handler.pfds + *i, m->handler.pfds + *i + 1,
+ (m->handler.pfdcount - *i - 1) * sizeof(struct pollfd));
+ m->handler.pfdcount--;
+ m->handler.pfds[m->handler.pfdcount].fd = 0;
+ m->handler.pfds[m->handler.pfdcount].events = 0;
+
+ memmove(pfds + *i, pfds + *i + 1,
+ (m->handler.copycount - *i - 1) * sizeof(struct pollfd));
+ m->handler.copycount--;
+ m->handler.copy[m->handler.copycount].fd = 0;
+ m->handler.copy[m->handler.copycount].events = 0;
+
+ *i = *i - 1;
+ }
+}
+
/**
* Process I/O events.
*
* Walks through file descriptor array looking for those pollfds whose .revents
* field has something interesting. Deletes any invalid file descriptors.
*
+ * Try to impart some impartiality to handling of io. The event
+ * system will cycle through the fd's available for io
+ * giving each one a chance to go first.
+ *
* @param m the thread master
* @param num the number of active file descriptors (return value of poll())
*/
@@ -1633,58 +1697,15 @@ static void thread_process_io(struct event_loop *m, unsigned int num)
{
unsigned int ready = 0;
struct pollfd *pfds = m->handler.copy;
+ nfds_t i, last_read = m->last_read % m->handler.copycount;
- for (nfds_t i = 0; i < m->handler.copycount && ready < num; ++i) {
- /* no event for current fd? immediately continue */
- if (pfds[i].revents == 0)
- continue;
+ for (i = last_read; i < m->handler.copycount && ready < num; ++i)
+ thread_process_io_inner_loop(m, num, pfds, &i, &ready);
- ready++;
+ for (i = 0; i < last_read && ready < num; ++i)
+ thread_process_io_inner_loop(m, num, pfds, &i, &ready);
- /*
- * Unless someone has called event_cancel from another
- * pthread, the only thing that could have changed in
- * m->handler.pfds while we were asleep is the .events
- * field in a given pollfd. Barring event_cancel() that
- * value should be a superset of the values we have in our
- * copy, so there's no need to update it. Similarily,
- * barring deletion, the fd should still be a valid index
- * into the master's pfds.
- *
- * We are including POLLERR here to do a READ event
- * this is because the read should fail and the
- * read function should handle it appropriately
- */
- if (pfds[i].revents & (POLLIN | POLLHUP | POLLERR)) {
- thread_process_io_helper(m, m->read[pfds[i].fd], POLLIN,
- pfds[i].revents, i);
- }
- if (pfds[i].revents & POLLOUT)
- thread_process_io_helper(m, m->write[pfds[i].fd],
- POLLOUT, pfds[i].revents, i);
-
- /*
- * if one of our file descriptors is garbage, remove the same
- * from both pfds + update sizes and index
- */
- if (pfds[i].revents & POLLNVAL) {
- memmove(m->handler.pfds + i, m->handler.pfds + i + 1,
- (m->handler.pfdcount - i - 1)
- * sizeof(struct pollfd));
- m->handler.pfdcount--;
- m->handler.pfds[m->handler.pfdcount].fd = 0;
- m->handler.pfds[m->handler.pfdcount].events = 0;
-
- memmove(pfds + i, pfds + i + 1,
- (m->handler.copycount - i - 1)
- * sizeof(struct pollfd));
- m->handler.copycount--;
- m->handler.copy[m->handler.copycount].fd = 0;
- m->handler.copy[m->handler.copycount].events = 0;
-
- i--;
- }
- }
+ m->last_read++;
}
/* Add all timers that have popped to the ready list. */
@@ -1864,12 +1885,6 @@ struct event *event_fetch(struct event_loop *m, struct event *fetch)
return fetch;
}
-static unsigned long timeval_elapsed(struct timeval a, struct timeval b)
-{
- return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO)
- + (a.tv_usec - b.tv_usec));
-}
-
unsigned long event_consumed_time(RUSAGE_T *now, RUSAGE_T *start,
unsigned long *cputime)
{
@@ -1972,6 +1987,7 @@ void event_getrusage(RUSAGE_T *r)
void event_call(struct event *thread)
{
RUSAGE_T before, after;
+ bool suppress_warnings = EVENT_ARG(thread);
/* if the thread being called is the CLI, it may change cputime_enabled
* ("service cputime-stats" command), which can result in nonsensical
@@ -2032,6 +2048,9 @@ void event_call(struct event *thread)
atomic_fetch_or_explicit(&thread->hist->types, 1 << thread->add_type,
memory_order_seq_cst);
+ if (suppress_warnings)
+ return;
+
if (cputime_enabled_here && cputime_enabled && cputime_threshold
&& cputime > cputime_threshold) {
/*
@@ -2066,10 +2085,15 @@ void event_call(struct event *thread)
/* Execute thread */
void _event_execute(const struct xref_eventsched *xref, struct event_loop *m,
- void (*func)(struct event *), void *arg, int val)
+ void (*func)(struct event *), void *arg, int val,
+ struct event **eref)
{
struct event *thread;
+ /* Cancel existing scheduled task TODO -- nice to do in 1 lock cycle */
+ if (eref)
+ event_cancel(eref);
+
/* Get or allocate new thread to execute. */
frr_with_mutex (&m->mtx) {
thread = thread_get(m, EVENT_EVENT, func, arg, xref);
diff --git a/lib/explicit_bzero.c b/lib/explicit_bzero.c
index fa64ed85bf..e838f95e65 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 f86adab5d6..5a0790f8bf 100644
--- a/lib/filter.c
+++ b/lib/filter.c
@@ -410,7 +410,10 @@ void access_list_filter_add(struct access_list *access,
filter->prev = access->tail;
access->tail = filter;
}
+}
+void access_list_filter_update(struct access_list *access)
+{
/* Run hook function. */
if (access->master->add_hook)
(*access->master->add_hook)(access);
@@ -885,7 +888,7 @@ static void access_list_init_ipv6(void)
install_element(ENABLE_NODE, &show_ipv6_access_list_name_cmd);
}
-void access_list_init(void)
+void access_list_init_new(bool in_backend)
{
cmd_variable_handler_register(access_list_handlers);
@@ -893,7 +896,15 @@ void access_list_init(void)
access_list_init_ipv6();
access_list_init_mac();
- filter_cli_init();
+ if (!in_backend) {
+ /* we do not want to handle config commands in the backend */
+ filter_cli_init();
+ }
+}
+
+void access_list_init(void)
+{
+ access_list_init_new(false);
}
void access_list_reset(void)
diff --git a/lib/filter.h b/lib/filter.h
index e092f0771a..4fa482ba4e 100644
--- a/lib/filter.h
+++ b/lib/filter.h
@@ -114,6 +114,7 @@ struct access_master {
/* Prototypes for access-list. */
extern void access_list_init(void);
+extern void access_list_init_new(bool in_backend);
extern void access_list_reset(void);
extern void access_list_add_hook(void (*func)(struct access_list *));
extern void access_list_delete_hook(void (*func)(struct access_list *));
@@ -124,13 +125,14 @@ extern enum filter_type access_list_apply(struct access_list *access,
struct access_list *access_list_get(afi_t afi, const char *name);
void access_list_delete(struct access_list *access);
struct filter *filter_new(void);
-void access_list_filter_add(struct access_list *access,
- struct filter *filter);
+void access_list_filter_add(struct access_list *access, struct filter *filter);
void access_list_filter_delete(struct access_list *access,
struct filter *filter);
+void access_list_filter_update(struct access_list *access);
int64_t filter_new_seq_get(struct access_list *access);
extern const struct frr_yang_module_info frr_filter_info;
+extern const struct frr_yang_module_info frr_filter_cli_info;
/* filter_nb.c */
diff --git a/lib/filter_cli.c b/lib/filter_cli.c
index 5c3dc5e49d..c40c2a75fe 100644
--- a/lib/filter_cli.c
+++ b/lib/filter_cli.c
@@ -69,59 +69,66 @@ static int64_t acl_get_seq(struct vty *vty, const char *xpath, bool is_remove)
return seq;
}
-static int acl_remove_if_empty(struct vty *vty, const char *iptype,
- const char *name)
+/**
+ * Remove main data structure filter list if there are no more entries or
+ * remark. This fixes compatibility with old CLI and tests.
+ */
+static int filter_remove_check_empty(struct vty *vty, const char *ftype,
+ const char *iptype, const char *name,
+ uint32_t del_seq, bool del_remark)
{
+ const struct lyd_node *remark_dnode = NULL;
+ const struct lyd_node *entry_dnode = NULL;
char xpath[XPATH_MAXLEN];
+ uint32_t count;
+
+ /* Count existing entries */
+ count = yang_dnode_count(vty->candidate_config->dnode,
+ "/frr-filter:lib/%s-list[type='%s'][name='%s']/entry",
+ ftype, iptype, name);
+
+ /* Check entry-to-delete actually exists */
+ if (del_seq) {
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/%s-list[type='%s'][name='%s']/entry[sequence='%u']",
+ ftype, iptype, name, del_seq);
+ entry_dnode = yang_dnode_get(vty->candidate_config->dnode,
+ xpath);
+
+ /* If exists, delete and don't count it, we need only remaining entries */
+ if (entry_dnode) {
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ count--;
+ }
+ }
+ /* Delete the remark, or check whether it exists if we're keeping it */
snprintf(xpath, sizeof(xpath),
- "/frr-filter:lib/access-list[type='%s'][name='%s']/remark",
+ "/frr-filter:lib/%s-list[type='%s'][name='%s']/remark", ftype,
iptype, name);
- /* List is not empty if there is a remark, check that: */
- if (yang_dnode_exists(vty->candidate_config->dnode, xpath))
- return CMD_SUCCESS;
-
- /* Check if we have any entries: */
- snprintf(xpath, sizeof(xpath),
- "/frr-filter:lib/access-list[type='%s'][name='%s']", iptype,
- name);
- /*
- * NOTE: if the list is empty it will return the first sequence
- * number: 5.
- */
- if (acl_get_seq(vty, xpath, true) != 5)
- return CMD_SUCCESS;
+ if (del_remark)
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ else
+ remark_dnode = yang_dnode_get(vty->candidate_config->dnode,
+ xpath);
+
+ /* If there are no entries left and no remark, delete the whole list */
+ if (count == 0 && !remark_dnode) {
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/%s-list[type='%s'][name='%s']", ftype,
+ iptype, name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ }
- /* Nobody is using this list, lets remove it. */
- nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, NULL);
}
-static int acl_remove(struct vty *vty, const char *iptype, const char *name,
- int64_t sseq)
-{
- char xpath[XPATH_MAXLEN];
- int rv;
-
- snprintfrr(
- xpath, sizeof(xpath),
- "/frr-filter:lib/access-list[type='%s'][name='%s']/entry[sequence='%" PRId64 "']",
- iptype, name, sseq);
- nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-
- rv = nb_cli_apply_changes(vty, NULL);
- if (rv == CMD_SUCCESS)
- return acl_remove_if_empty(vty, iptype, name);
-
- return rv;
-}
-
/*
* Cisco (legacy) access lists.
*/
DEFPY_YANG(
access_list_std, access_list_std_cmd,
- "access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask>",
+ "access-list ACCESSLIST4_NAME$name [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask>",
ACCESS_LIST_STR
ACCESS_LIST_ZEBRA_STR
ACCESS_LIST_SEQ_STR
@@ -197,7 +204,7 @@ DEFPY_YANG(
DEFPY_YANG(
no_access_list_std, no_access_list_std_cmd,
- "no access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask>",
+ "no access-list ACCESSLIST4_NAME$name [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask>",
NO_STR
ACCESS_LIST_STR
ACCESS_LIST_ZEBRA_STR
@@ -213,7 +220,8 @@ DEFPY_YANG(
/* If the user provided sequence number, then just go for it. */
if (seq_str != NULL)
- return acl_remove(vty, "ipv4", name, seq);
+ return filter_remove_check_empty(vty, "access", "ipv4", name,
+ seq, false);
/* Otherwise, to keep compatibility, we need to figure it out. */
ada.ada_type = "ipv4";
@@ -237,12 +245,13 @@ DEFPY_YANG(
else
return CMD_WARNING_CONFIG_FAILED;
- return acl_remove(vty, "ipv4", name, sseq);
+ return filter_remove_check_empty(vty, "access", "ipv4", name, sseq,
+ false);
}
DEFPY_YANG(
access_list_ext, access_list_ext_cmd,
- "access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>",
+ "access-list ACCESSLIST4_NAME$name [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>",
ACCESS_LIST_STR
ACCESS_LIST_ZEBRA_STR
ACCESS_LIST_SEQ_STR
@@ -360,7 +369,7 @@ DEFPY_YANG(
DEFPY_YANG(
no_access_list_ext, no_access_list_ext_cmd,
- "no access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>",
+ "no access-list ACCESSLIST4_NAME$name [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>",
NO_STR
ACCESS_LIST_STR
ACCESS_LIST_ZEBRA_STR
@@ -384,7 +393,8 @@ DEFPY_YANG(
/* If the user provided sequence number, then just go for it. */
if (seq_str != NULL)
- return acl_remove(vty, "ipv4", name, seq);
+ return filter_remove_check_empty(vty, "access", "ipv4", name,
+ seq, false);
/* Otherwise, to keep compatibility, we need to figure it out. */
ada.ada_type = "ipv4";
@@ -429,7 +439,8 @@ DEFPY_YANG(
else
return CMD_WARNING_CONFIG_FAILED;
- return acl_remove(vty, "ipv4", name, sseq);
+ return filter_remove_check_empty(vty, "access", "ipv4", name, sseq,
+ false);
}
/*
@@ -437,7 +448,7 @@ DEFPY_YANG(
*/
DEFPY_YANG(
access_list, access_list_cmd,
- "access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <A.B.C.D/M$prefix [exact-match$exact]|any>",
+ "access-list ACCESSLIST4_NAME$name [seq (1-4294967295)$seq] <deny|permit>$action <A.B.C.D/M$prefix [exact-match$exact]|any>",
ACCESS_LIST_STR
ACCESS_LIST_ZEBRA_STR
ACCESS_LIST_SEQ_STR
@@ -510,7 +521,7 @@ DEFPY_YANG(
DEFPY_YANG(
no_access_list, no_access_list_cmd,
- "no access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <A.B.C.D/M$prefix [exact-match$exact]|any>",
+ "no access-list ACCESSLIST4_NAME$name [seq (1-4294967295)$seq] <deny|permit>$action <A.B.C.D/M$prefix [exact-match$exact]|any>",
NO_STR
ACCESS_LIST_STR
ACCESS_LIST_ZEBRA_STR
@@ -525,7 +536,8 @@ DEFPY_YANG(
/* If the user provided sequence number, then just go for it. */
if (seq_str != NULL)
- return acl_remove(vty, "ipv4", name, seq);
+ return filter_remove_check_empty(vty, "access", "ipv4", name,
+ seq, false);
/* Otherwise, to keep compatibility, we need to figure it out. */
ada.ada_type = "ipv4";
@@ -549,12 +561,13 @@ DEFPY_YANG(
else
return CMD_WARNING_CONFIG_FAILED;
- return acl_remove(vty, "ipv4", name, sseq);
+ return filter_remove_check_empty(vty, "access", "ipv4", name, sseq,
+ false);
}
DEFPY_YANG(
no_access_list_all, no_access_list_all_cmd,
- "no access-list WORD$name",
+ "no access-list ACCESSLIST4_NAME$name",
NO_STR
ACCESS_LIST_STR
ACCESS_LIST_ZEBRA_STR)
@@ -570,7 +583,7 @@ DEFPY_YANG(
DEFPY_YANG(
access_list_remark, access_list_remark_cmd,
- "access-list WORD$name remark LINE...",
+ "access-list ACCESSLIST4_NAME$name remark LINE...",
ACCESS_LIST_STR
ACCESS_LIST_ZEBRA_STR
ACCESS_LIST_REMARK_STR
@@ -594,30 +607,18 @@ DEFPY_YANG(
DEFPY_YANG(
no_access_list_remark, no_access_list_remark_cmd,
- "no access-list WORD$name remark",
+ "no access-list ACCESSLIST4_NAME$name remark",
NO_STR
ACCESS_LIST_STR
ACCESS_LIST_ZEBRA_STR
ACCESS_LIST_REMARK_STR)
{
- char xpath[XPATH_MAXLEN];
- int rv;
-
- snprintf(xpath, sizeof(xpath),
- "/frr-filter:lib/access-list[type='ipv4'][name='%s']/remark",
- name);
- nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-
- rv = nb_cli_apply_changes(vty, NULL);
- if (rv == CMD_SUCCESS)
- return acl_remove_if_empty(vty, "ipv4", name);
-
- return rv;
+ return filter_remove_check_empty(vty, "access", "ipv4", name, 0, true);
}
ALIAS(
no_access_list_remark, no_access_list_remark_line_cmd,
- "no access-list WORD$name remark LINE...",
+ "no access-list ACCESSLIST4_NAME$name remark LINE...",
NO_STR
ACCESS_LIST_STR
ACCESS_LIST_ZEBRA_STR
@@ -626,7 +627,7 @@ ALIAS(
DEFPY_YANG(
ipv6_access_list, ipv6_access_list_cmd,
- "ipv6 access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X::X:X/M$prefix [exact-match$exact]|any>",
+ "ipv6 access-list ACCESSLIST6_NAME$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X::X:X/M$prefix [exact-match$exact]|any>",
IPV6_STR
ACCESS_LIST_STR
ACCESS_LIST_ZEBRA_STR
@@ -700,7 +701,7 @@ DEFPY_YANG(
DEFPY_YANG(
no_ipv6_access_list, no_ipv6_access_list_cmd,
- "no ipv6 access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X::X:X/M$prefix [exact-match$exact]|any>",
+ "no ipv6 access-list ACCESSLIST6_NAME$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X::X:X/M$prefix [exact-match$exact]|any>",
NO_STR
IPV6_STR
ACCESS_LIST_STR
@@ -716,7 +717,8 @@ DEFPY_YANG(
/* If the user provided sequence number, then just go for it. */
if (seq_str != NULL)
- return acl_remove(vty, "ipv6", name, seq);
+ return filter_remove_check_empty(vty, "access", "ipv6", name,
+ seq, false);
/* Otherwise, to keep compatibility, we need to figure it out. */
ada.ada_type = "ipv6";
@@ -740,12 +742,13 @@ DEFPY_YANG(
else
return CMD_WARNING_CONFIG_FAILED;
- return acl_remove(vty, "ipv6", name, sseq);
+ return filter_remove_check_empty(vty, "access", "ipv6", name, sseq,
+ false);
}
DEFPY_YANG(
no_ipv6_access_list_all, no_ipv6_access_list_all_cmd,
- "no ipv6 access-list WORD$name",
+ "no ipv6 access-list ACCESSLIST6_NAME$name",
NO_STR
IPV6_STR
ACCESS_LIST_STR
@@ -762,7 +765,7 @@ DEFPY_YANG(
DEFPY_YANG(
ipv6_access_list_remark, ipv6_access_list_remark_cmd,
- "ipv6 access-list WORD$name remark LINE...",
+ "ipv6 access-list ACCESSLIST6_NAME$name remark LINE...",
IPV6_STR
ACCESS_LIST_STR
ACCESS_LIST_ZEBRA_STR
@@ -787,26 +790,14 @@ DEFPY_YANG(
DEFPY_YANG(
no_ipv6_access_list_remark, no_ipv6_access_list_remark_cmd,
- "no ipv6 access-list WORD$name remark",
+ "no ipv6 access-list ACCESSLIST6_NAME$name remark",
NO_STR
IPV6_STR
ACCESS_LIST_STR
ACCESS_LIST_ZEBRA_STR
ACCESS_LIST_REMARK_STR)
{
- char xpath[XPATH_MAXLEN];
- int rv;
-
- snprintf(xpath, sizeof(xpath),
- "/frr-filter:lib/access-list[type='ipv6'][name='%s']/remark",
- name);
- nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-
- rv = nb_cli_apply_changes(vty, NULL);
- if (rv == CMD_SUCCESS)
- return acl_remove_if_empty(vty, "ipv6", name);
-
- return rv;
+ return filter_remove_check_empty(vty, "access", "ipv6", name, 0, true);
}
ALIAS(
@@ -902,7 +893,8 @@ DEFPY_YANG(
/* If the user provided sequence number, then just go for it. */
if (seq_str != NULL)
- return acl_remove(vty, "mac", name, seq);
+ return filter_remove_check_empty(vty, "access", "mac", name,
+ seq, false);
/* Otherwise, to keep compatibility, we need to figure it out. */
ada.ada_type = "mac";
@@ -922,7 +914,8 @@ DEFPY_YANG(
else
return CMD_WARNING_CONFIG_FAILED;
- return acl_remove(vty, "mac", name, sseq);
+ return filter_remove_check_empty(vty, "access", "mac", name, sseq,
+ false);
}
DEFPY_YANG(
@@ -976,19 +969,7 @@ DEFPY_YANG(
ACCESS_LIST_ZEBRA_STR
ACCESS_LIST_REMARK_STR)
{
- char xpath[XPATH_MAXLEN];
- int rv;
-
- snprintf(xpath, sizeof(xpath),
- "/frr-filter:lib/access-list[type='mac'][name='%s']/remark",
- name);
- nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-
- rv = nb_cli_apply_changes(vty, NULL);
- if (rv == CMD_SUCCESS)
- return acl_remove_if_empty(vty, "mac", name);
-
- return rv;
+ return filter_remove_check_empty(vty, "access", "mac", name, 0, true);
}
ALIAS(
@@ -1004,8 +985,8 @@ ALIAS(
int access_list_cmp(const struct lyd_node *dnode1,
const struct lyd_node *dnode2)
{
- uint32_t seq1 = yang_dnode_get_uint32(dnode1, "./sequence");
- uint32_t seq2 = yang_dnode_get_uint32(dnode2, "./sequence");
+ uint32_t seq1 = yang_dnode_get_uint32(dnode1, "sequence");
+ uint32_t seq2 = yang_dnode_get_uint32(dnode2, "sequence");
return seq1 - seq2;
}
@@ -1022,23 +1003,23 @@ void access_list_show(struct vty *vty, const struct lyd_node *dnode,
struct in_addr addr, mask;
char macstr[PREFIX2STR_BUFFER];
- is_any = yang_dnode_exists(dnode, "./any");
+ is_any = yang_dnode_exists(dnode, "any");
switch (type) {
case YALT_IPV4:
if (is_any)
break;
- if (yang_dnode_exists(dnode, "./host")
- || yang_dnode_exists(dnode, "./network/address")
- || yang_dnode_exists(dnode, "./source-any")) {
+ if (yang_dnode_exists(dnode, "host")
+ || yang_dnode_exists(dnode, "network/address")
+ || yang_dnode_exists(dnode, "source-any")) {
cisco_style = true;
- if (yang_dnode_exists(dnode, "./destination-host")
+ if (yang_dnode_exists(dnode, "destination-host")
|| yang_dnode_exists(
dnode, "./destination-network/address")
- || yang_dnode_exists(dnode, "./destination-any"))
+ || yang_dnode_exists(dnode, "destination-any"))
cisco_extended = true;
} else {
- yang_dnode_get_prefix(&p, dnode, "./ipv4-prefix");
+ yang_dnode_get_prefix(&p, dnode, "ipv4-prefix");
is_exact = yang_dnode_get_bool(dnode,
"./ipv4-exact-match");
}
@@ -1048,39 +1029,39 @@ void access_list_show(struct vty *vty, const struct lyd_node *dnode,
if (is_any)
break;
- yang_dnode_get_prefix(&p, dnode, "./ipv6-prefix");
- is_exact = yang_dnode_get_bool(dnode, "./ipv6-exact-match");
+ yang_dnode_get_prefix(&p, dnode, "ipv6-prefix");
+ is_exact = yang_dnode_get_bool(dnode, "ipv6-exact-match");
break;
case YALT_MAC: /* mac */
vty_out(vty, "mac ");
if (is_any)
break;
- yang_dnode_get_prefix(&p, dnode, "./mac");
+ yang_dnode_get_prefix(&p, dnode, "mac");
break;
}
vty_out(vty, "access-list %s seq %s %s",
yang_dnode_get_string(dnode, "../name"),
- yang_dnode_get_string(dnode, "./sequence"),
- yang_dnode_get_string(dnode, "./action"));
+ yang_dnode_get_string(dnode, "sequence"),
+ yang_dnode_get_string(dnode, "action"));
/* Handle Cisco style access lists. */
if (cisco_style) {
if (cisco_extended)
vty_out(vty, " ip");
- if (yang_dnode_exists(dnode, "./network")) {
- yang_dnode_get_ipv4(&addr, dnode, "./network/address");
- yang_dnode_get_ipv4(&mask, dnode, "./network/mask");
+ if (yang_dnode_exists(dnode, "network")) {
+ yang_dnode_get_ipv4(&addr, dnode, "network/address");
+ yang_dnode_get_ipv4(&mask, dnode, "network/mask");
vty_out(vty, " %pI4 %pI4", &addr, &mask);
- } else if (yang_dnode_exists(dnode, "./host")) {
+ } else if (yang_dnode_exists(dnode, "host")) {
if (cisco_extended)
vty_out(vty, " host");
vty_out(vty, " %s",
- yang_dnode_get_string(dnode, "./host"));
- } else if (yang_dnode_exists(dnode, "./source-any"))
+ yang_dnode_get_string(dnode, "host"));
+ } else if (yang_dnode_exists(dnode, "source-any"))
vty_out(vty, " any");
/* Not extended, exit earlier. */
@@ -1090,17 +1071,17 @@ void access_list_show(struct vty *vty, const struct lyd_node *dnode,
}
/* Handle destination address. */
- if (yang_dnode_exists(dnode, "./destination-network")) {
+ if (yang_dnode_exists(dnode, "destination-network")) {
yang_dnode_get_ipv4(&addr, dnode,
"./destination-network/address");
yang_dnode_get_ipv4(&mask, dnode,
"./destination-network/mask");
vty_out(vty, " %pI4 %pI4", &addr, &mask);
- } else if (yang_dnode_exists(dnode, "./destination-host"))
+ } else if (yang_dnode_exists(dnode, "destination-host"))
vty_out(vty, " host %s",
yang_dnode_get_string(dnode,
"./destination-host"));
- else if (yang_dnode_exists(dnode, "./destination-any"))
+ else if (yang_dnode_exists(dnode, "destination-any"))
vty_out(vty, " any");
vty_out(vty, "\n");
@@ -1149,62 +1130,17 @@ void access_list_remark_show(struct vty *vty, const struct lyd_node *dnode,
* Prefix lists.
*/
-/**
- * Remove main data structure prefix list if there are no more entries or
- * remark. This fixes compatibility with old CLI and tests.
- */
-static int plist_remove_if_empty(struct vty *vty, const char *iptype,
- const char *name)
-{
- char xpath[XPATH_MAXLEN];
-
- snprintf(xpath, sizeof(xpath),
- "/frr-filter:lib/prefix-list[type='%s'][name='%s']/remark",
- iptype, name);
- /* List is not empty if there is a remark, check that: */
- if (yang_dnode_exists(vty->candidate_config->dnode, xpath))
- return CMD_SUCCESS;
-
- /* Check if we have any entries: */
- snprintf(xpath, sizeof(xpath),
- "/frr-filter:lib/prefix-list[type='%s'][name='%s']", iptype,
- name);
- /*
- * NOTE: if the list is empty it will return the first sequence
- * number: 5.
- */
- if (acl_get_seq(vty, xpath, true) != 5)
- return CMD_SUCCESS;
-
- /* Nobody is using this list, lets remove it. */
- nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
- return nb_cli_apply_changes(vty, NULL);
-}
-
static int plist_remove(struct vty *vty, const char *iptype, const char *name,
- const char *seq, const char *action,
+ uint32_t seq, const char *action,
union prefixconstptr prefix, int ge, int le)
{
int64_t sseq;
struct plist_dup_args pda = {};
- char xpath[XPATH_MAXLEN];
- char xpath_entry[XPATH_MAXLEN + 32];
- int rv;
/* If the user provided sequence number, then just go for it. */
- if (seq != NULL) {
- snprintf(
- xpath, sizeof(xpath),
- "/frr-filter:lib/prefix-list[type='%s'][name='%s']/entry[sequence='%s']",
- iptype, name, seq);
- nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-
- rv = nb_cli_apply_changes(vty, NULL);
- if (rv == CMD_SUCCESS)
- return plist_remove_if_empty(vty, iptype, name);
-
- return rv;
- }
+ if (seq != 0)
+ return filter_remove_check_empty(vty, "prefix", iptype, name,
+ seq, false);
/* Otherwise, to keep compatibility, we need to figure it out. */
pda.pda_type = iptype;
@@ -1224,22 +1160,13 @@ static int plist_remove(struct vty *vty, const char *iptype, const char *name,
else
return CMD_WARNING_CONFIG_FAILED;
- snprintfrr(
- xpath_entry, sizeof(xpath_entry),
- "/frr-filter:lib/prefix-list[type='%s'][name='%s']/entry[sequence='%" PRId64 "']",
- iptype, name, sseq);
- nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL);
-
- rv = nb_cli_apply_changes(vty, NULL);
- if (rv == CMD_SUCCESS)
- return plist_remove_if_empty(vty, iptype, name);
-
- return rv;
+ return filter_remove_check_empty(vty, "prefix", iptype, name, sseq,
+ false);
}
DEFPY_YANG(
ip_prefix_list, ip_prefix_list_cmd,
- "ip prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|A.B.C.D/M$prefix [{ge (0-32)$ge|le (0-32)$le}]>",
+ "ip prefix-list PREFIXLIST4_NAME$name [seq (1-4294967295)$seq] <deny|permit>$action <any|A.B.C.D/M$prefix [{ge (0-32)$ge|le (0-32)$le}]>",
IP_STR
PREFIX_LIST_STR
PREFIX_LIST_NAME_STR
@@ -1333,7 +1260,7 @@ DEFPY_YANG(
DEFPY_YANG(
no_ip_prefix_list, no_ip_prefix_list_cmd,
- "no ip prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|A.B.C.D/M$prefix [{ge (0-32)|le (0-32)}]>",
+ "no ip prefix-list PREFIXLIST4_NAME$name [seq (1-4294967295)$seq] <deny|permit>$action <any|A.B.C.D/M$prefix [{ge (0-32)|le (0-32)}]>",
NO_STR
IP_STR
PREFIX_LIST_STR
@@ -1347,25 +1274,25 @@ DEFPY_YANG(
"Maximum prefix length to be matched\n"
"Maximum prefix length\n")
{
- return plist_remove(vty, "ipv4", name, seq_str, action,
+ return plist_remove(vty, "ipv4", name, seq, action,
prefix_str ? prefix : NULL, ge, le);
}
DEFPY_YANG(
no_ip_prefix_list_seq, no_ip_prefix_list_seq_cmd,
- "no ip prefix-list WORD$name seq (1-4294967295)$seq",
+ "no ip prefix-list PREFIXLIST4_NAME$name seq (1-4294967295)$seq",
NO_STR
IP_STR
PREFIX_LIST_STR
PREFIX_LIST_NAME_STR
ACCESS_LIST_SEQ_STR)
{
- return plist_remove(vty, "ipv4", name, seq_str, NULL, NULL, 0, 0);
+ return plist_remove(vty, "ipv4", name, seq, NULL, NULL, 0, 0);
}
DEFPY_YANG(
no_ip_prefix_list_all, no_ip_prefix_list_all_cmd,
- "no ip prefix-list WORD$name",
+ "no ip prefix-list PREFIXLIST4_NAME$name",
NO_STR
IP_STR
PREFIX_LIST_STR
@@ -1382,7 +1309,7 @@ DEFPY_YANG(
DEFPY_YANG(
ip_prefix_list_remark, ip_prefix_list_remark_cmd,
- "ip prefix-list WORD$name description LINE...",
+ "ip prefix-list PREFIXLIST4_NAME$name description LINE...",
IP_STR
PREFIX_LIST_STR
PREFIX_LIST_NAME_STR
@@ -1407,31 +1334,19 @@ DEFPY_YANG(
DEFPY_YANG(
no_ip_prefix_list_remark, no_ip_prefix_list_remark_cmd,
- "no ip prefix-list WORD$name description",
+ "no ip prefix-list PREFIXLIST4_NAME$name description",
NO_STR
IP_STR
PREFIX_LIST_STR
PREFIX_LIST_NAME_STR
ACCESS_LIST_REMARK_STR)
{
- char xpath[XPATH_MAXLEN];
- int rv;
-
- snprintf(xpath, sizeof(xpath),
- "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']/remark",
- name);
- nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-
- rv = nb_cli_apply_changes(vty, NULL);
- if (rv == CMD_SUCCESS)
- return plist_remove_if_empty(vty, "ipv4", name);
-
- return rv;
+ return filter_remove_check_empty(vty, "prefix", "ipv4", name, 0, true);
}
ALIAS(
no_ip_prefix_list_remark, no_ip_prefix_list_remark_line_cmd,
- "no ip prefix-list WORD$name description LINE...",
+ "no ip prefix-list PREFIXLIST4_NAME$name description LINE...",
NO_STR
IP_STR
PREFIX_LIST_STR
@@ -1441,7 +1356,7 @@ ALIAS(
DEFPY_YANG(
ipv6_prefix_list, ipv6_prefix_list_cmd,
- "ipv6 prefix-list WORD$name [seq (1-4294967295)] <deny|permit>$action <any|X:X::X:X/M$prefix [{ge (0-128)$ge|le (0-128)$le}]>",
+ "ipv6 prefix-list PREFIXLIST6_NAME$name [seq (1-4294967295)] <deny|permit>$action <any|X:X::X:X/M$prefix [{ge (0-128)$ge|le (0-128)$le}]>",
IPV6_STR
PREFIX_LIST_STR
PREFIX_LIST_NAME_STR
@@ -1535,7 +1450,7 @@ DEFPY_YANG(
DEFPY_YANG(
no_ipv6_prefix_list, no_ipv6_prefix_list_cmd,
- "no ipv6 prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|X:X::X:X/M$prefix [{ge (0-128)$ge|le (0-128)$le}]>",
+ "no ipv6 prefix-list PREFIXLIST6_NAME$name [seq (1-4294967295)$seq] <deny|permit>$action <any|X:X::X:X/M$prefix [{ge (0-128)$ge|le (0-128)$le}]>",
NO_STR
IPV6_STR
PREFIX_LIST_STR
@@ -1549,25 +1464,25 @@ DEFPY_YANG(
"Minimum prefix length to be matched\n"
"Minimum prefix length\n")
{
- return plist_remove(vty, "ipv6", name, seq_str, action,
+ return plist_remove(vty, "ipv6", name, seq, action,
prefix_str ? prefix : NULL, ge, le);
}
DEFPY_YANG(
no_ipv6_prefix_list_seq, no_ipv6_prefix_list_seq_cmd,
- "no ipv6 prefix-list WORD$name seq (1-4294967295)$seq",
+ "no ipv6 prefix-list PREFIXLIST6_NAME$name seq (1-4294967295)$seq",
NO_STR
IPV6_STR
PREFIX_LIST_STR
PREFIX_LIST_NAME_STR
ACCESS_LIST_SEQ_STR)
{
- return plist_remove(vty, "ipv6", name, seq_str, NULL, NULL, 0, 0);
+ return plist_remove(vty, "ipv6", name, seq, NULL, NULL, 0, 0);
}
DEFPY_YANG(
no_ipv6_prefix_list_all, no_ipv6_prefix_list_all_cmd,
- "no ipv6 prefix-list WORD$name",
+ "no ipv6 prefix-list PREFIXLIST6_NAME$name",
NO_STR
IPV6_STR
PREFIX_LIST_STR
@@ -1584,7 +1499,7 @@ DEFPY_YANG(
DEFPY_YANG(
ipv6_prefix_list_remark, ipv6_prefix_list_remark_cmd,
- "ipv6 prefix-list WORD$name description LINE...",
+ "ipv6 prefix-list PREFIXLIST6_NAME$name description LINE...",
IPV6_STR
PREFIX_LIST_STR
PREFIX_LIST_NAME_STR
@@ -1609,31 +1524,19 @@ DEFPY_YANG(
DEFPY_YANG(
no_ipv6_prefix_list_remark, no_ipv6_prefix_list_remark_cmd,
- "no ipv6 prefix-list WORD$name description",
+ "no ipv6 prefix-list PREFIXLIST6_NAME$name description",
NO_STR
IPV6_STR
PREFIX_LIST_STR
PREFIX_LIST_NAME_STR
ACCESS_LIST_REMARK_STR)
{
- char xpath[XPATH_MAXLEN];
- int rv;
-
- snprintf(xpath, sizeof(xpath),
- "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']/remark",
- name);
- nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-
- rv = nb_cli_apply_changes(vty, NULL);
- if (rv == CMD_SUCCESS)
- return plist_remove_if_empty(vty, "ipv6", name);
-
- return rv;
+ return filter_remove_check_empty(vty, "prefix", "ipv6", name, 0, true);
}
ALIAS(
no_ipv6_prefix_list_remark, no_ipv6_prefix_list_remark_line_cmd,
- "no ipv6 prefix-list WORD$name description LINE...",
+ "no ipv6 prefix-list PREFIXLIST6_NAME$name description LINE...",
NO_STR
IPV6_STR
PREFIX_LIST_STR
@@ -1644,8 +1547,8 @@ ALIAS(
int prefix_list_cmp(const struct lyd_node *dnode1,
const struct lyd_node *dnode2)
{
- uint32_t seq1 = yang_dnode_get_uint32(dnode1, "./sequence");
- uint32_t seq2 = yang_dnode_get_uint32(dnode2, "./sequence");
+ uint32_t seq1 = yang_dnode_get_uint32(dnode1, "sequence");
+ uint32_t seq2 = yang_dnode_get_uint32(dnode2, "sequence");
return seq1 - seq2;
}
@@ -1658,11 +1561,11 @@ void prefix_list_show(struct vty *vty, const struct lyd_node *dnode,
bool is_any;
struct prefix p;
- is_any = yang_dnode_exists(dnode, "./any");
+ is_any = yang_dnode_exists(dnode, "any");
switch (type) {
case YPLT_IPV4:
if (!is_any)
- yang_dnode_get_prefix(&p, dnode, "./ipv4-prefix");
+ yang_dnode_get_prefix(&p, dnode, "ipv4-prefix");
if (yang_dnode_exists(dnode,
"./ipv4-prefix-length-greater-or-equal"))
ge_str = yang_dnode_get_string(
@@ -1692,8 +1595,8 @@ void prefix_list_show(struct vty *vty, const struct lyd_node *dnode,
vty_out(vty, "prefix-list %s seq %s %s",
yang_dnode_get_string(dnode, "../name"),
- yang_dnode_get_string(dnode, "./sequence"),
- yang_dnode_get_string(dnode, "./action"));
+ yang_dnode_get_string(dnode, "sequence"),
+ yang_dnode_get_string(dnode, "action"));
if (is_any) {
vty_out(vty, " any\n");
diff --git a/lib/filter_nb.c b/lib/filter_nb.c
index 9511b8f5b5..39042d39ab 100644
--- a/lib/filter_nb.c
+++ b/lib/filter_nb.c
@@ -17,23 +17,6 @@
#include "lib/plist_int.h"
#include "lib/routemap.h"
-/* Helper function. */
-static void acl_notify_route_map(struct access_list *acl, int route_map_event)
-{
- switch (route_map_event) {
- case RMAP_EVENT_FILTER_ADDED:
- if (acl->master->add_hook)
- (*acl->master->add_hook)(acl);
- break;
- case RMAP_EVENT_FILTER_DELETED:
- if (acl->master->delete_hook)
- (*acl->master->delete_hook)(acl);
- break;
- }
-
- route_map_notify_dependencies(acl->name, route_map_event);
-}
-
static enum nb_error prefix_list_length_validate(struct nb_cb_modify_args *args)
{
int type = yang_dnode_get_enum(args->dnode, "../../type");
@@ -112,7 +95,7 @@ prefix_list_nb_validate_v4_af_type(const struct lyd_node *plist_dnode,
{
int af_type;
- af_type = yang_dnode_get_enum(plist_dnode, "./type");
+ af_type = yang_dnode_get_enum(plist_dnode, "type");
if (af_type != YPLT_IPV4) {
snprintf(errmsg, errmsg_len,
"prefix-list type %u is mismatched.", af_type);
@@ -128,7 +111,7 @@ prefix_list_nb_validate_v6_af_type(const struct lyd_node *plist_dnode,
{
int af_type;
- af_type = yang_dnode_get_enum(plist_dnode, "./type");
+ af_type = yang_dnode_get_enum(plist_dnode, "type");
if (af_type != YPLT_IPV6) {
snprintf(errmsg, errmsg_len,
"prefix-list type %u is mismatched.", af_type);
@@ -153,9 +136,6 @@ static int lib_prefix_list_entry_prefix_length_greater_or_equal_modify(
ple->ge = yang_dnode_get_uint8(args->dnode, NULL);
- /* Finish prefix entry update procedure. */
- prefix_list_entry_update_finish(ple);
-
return NB_OK;
}
@@ -174,9 +154,6 @@ static int lib_prefix_list_entry_prefix_length_lesser_or_equal_modify(
ple->le = yang_dnode_get_uint8(args->dnode, NULL);
- /* Finish prefix entry update procedure. */
- prefix_list_entry_update_finish(ple);
-
return NB_OK;
}
@@ -195,9 +172,6 @@ static int lib_prefix_list_entry_prefix_length_greater_or_equal_destroy(
ple->ge = 0;
- /* Finish prefix entry update procedure. */
- prefix_list_entry_update_finish(ple);
-
return NB_OK;
}
@@ -216,9 +190,6 @@ static int lib_prefix_list_entry_prefix_length_lesser_or_equal_destroy(
ple->le = 0;
- /* Finish prefix entry update procedure. */
- prefix_list_entry_update_finish(ple);
-
return NB_OK;
}
@@ -381,14 +352,14 @@ static void plist_dnode_to_prefix(const struct lyd_node *dnode, bool *any,
*ge = 0;
*le = 0;
- if (yang_dnode_exists(dnode, "./any")) {
+ if (yang_dnode_exists(dnode, "any")) {
*any = true;
return;
}
switch (yang_dnode_get_enum(dnode, "../type")) {
case YPLT_IPV4:
- yang_dnode_get_prefix(p, dnode, "./ipv4-prefix");
+ yang_dnode_get_prefix(p, dnode, "ipv4-prefix");
if (yang_dnode_exists(dnode,
"./ipv4-prefix-length-greater-or-equal"))
*ge = yang_dnode_get_uint8(
@@ -399,7 +370,7 @@ static void plist_dnode_to_prefix(const struct lyd_node *dnode, bool *any,
dnode, "./ipv4-prefix-length-lesser-or-equal");
break;
case YPLT_IPV6:
- yang_dnode_get_prefix(p, dnode, "./ipv6-prefix");
+ yang_dnode_get_prefix(p, dnode, "ipv6-prefix");
if (yang_dnode_exists(dnode,
"./ipv6-prefix-length-greater-or-equal"))
*ge = yang_dnode_get_uint8(
@@ -468,8 +439,8 @@ static int lib_access_list_create(struct nb_cb_create_args *args)
if (args->event != NB_EV_APPLY)
return NB_OK;
- type = yang_dnode_get_enum(args->dnode, "./type");
- acl_name = yang_dnode_get_string(args->dnode, "./name");
+ type = yang_dnode_get_enum(args->dnode, "type");
+ acl_name = yang_dnode_get_string(args->dnode, "name");
switch (type) {
case YALT_IPV4:
@@ -550,7 +521,7 @@ static int lib_access_list_entry_create(struct nb_cb_create_args *args)
return NB_OK;
f = filter_new();
- f->seq = yang_dnode_get_uint32(args->dnode, "./sequence");
+ f->seq = yang_dnode_get_uint32(args->dnode, "sequence");
acl = nb_running_get_entry(args->dnode, NULL, true);
f->acl = acl;
@@ -575,6 +546,15 @@ static int lib_access_list_entry_destroy(struct nb_cb_destroy_args *args)
return NB_OK;
}
+static void
+lib_access_list_entry_apply_finish(struct nb_cb_apply_finish_args *args)
+{
+ struct filter *f;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ access_list_filter_update(f->acl);
+}
+
/*
* XPath: /frr-filter:lib/access-list/entry/action
*/
@@ -594,8 +574,6 @@ lib_access_list_entry_action_modify(struct nb_cb_modify_args *args)
else
f->type = FILTER_DENY;
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -629,8 +607,6 @@ lib_access_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args)
fz = &f->u.zfilter;
yang_dnode_get_prefix(&fz->prefix, args->dnode, NULL);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -647,8 +623,6 @@ lib_access_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args)
fz = &f->u.zfilter;
memset(&fz->prefix, 0, sizeof(fz->prefix));
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED);
-
return NB_OK;
}
@@ -681,8 +655,6 @@ lib_access_list_entry_ipv4_exact_match_modify(struct nb_cb_modify_args *args)
fz = &f->u.zfilter;
fz->exact = yang_dnode_get_bool(args->dnode, NULL);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -699,8 +671,6 @@ lib_access_list_entry_ipv4_exact_match_destroy(struct nb_cb_destroy_args *args)
fz = &f->u.zfilter;
fz->exact = 0;
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED);
-
return NB_OK;
}
@@ -733,8 +703,6 @@ lib_access_list_entry_host_modify(struct nb_cb_modify_args *args)
yang_dnode_get_ipv4(&fc->addr, args->dnode, NULL);
fc->addr_mask.s_addr = CISCO_BIN_HOST_WILDCARD_MASK;
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -751,7 +719,29 @@ lib_access_list_entry_host_destroy(struct nb_cb_destroy_args *args)
fc = &f->u.cfilter;
cisco_unset_addr_mask(&fc->addr, &fc->addr_mask);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED);
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/access-list/entry/network
+ */
+static int lib_access_list_entry_network_create(struct nb_cb_create_args *args)
+{
+ /* Nothing to do here, everything is done in children callbacks */
+ return NB_OK;
+}
+
+static int lib_access_list_entry_network_destroy(struct nb_cb_destroy_args *args)
+{
+ struct filter_cisco *fc;
+ struct filter *f;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ fc = &f->u.cfilter;
+ cisco_unset_addr_mask(&fc->addr, &fc->addr_mask);
return NB_OK;
}
@@ -784,8 +774,6 @@ lib_access_list_entry_network_address_modify(struct nb_cb_modify_args *args)
fc = &f->u.cfilter;
yang_dnode_get_ipv4(&fc->addr, args->dnode, NULL);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -817,8 +805,6 @@ lib_access_list_entry_network_mask_modify(struct nb_cb_modify_args *args)
fc = &f->u.cfilter;
yang_dnode_get_ipv4(&fc->addr_mask, args->dnode, NULL);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -851,8 +837,6 @@ lib_access_list_entry_source_any_create(struct nb_cb_create_args *args)
fc->addr.s_addr = INADDR_ANY;
fc->addr_mask.s_addr = CISCO_BIN_ANY_WILDCARD_MASK;
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -869,8 +853,6 @@ lib_access_list_entry_source_any_destroy(struct nb_cb_destroy_args *args)
fc = &f->u.cfilter;
cisco_unset_addr_mask(&fc->addr, &fc->addr_mask);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED);
-
return NB_OK;
}
@@ -903,8 +885,6 @@ static int lib_access_list_entry_destination_host_modify(
yang_dnode_get_ipv4(&fc->mask, args->dnode, NULL);
fc->mask_mask.s_addr = CISCO_BIN_HOST_WILDCARD_MASK;
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -922,7 +902,32 @@ static int lib_access_list_entry_destination_host_destroy(
fc->extended = 0;
cisco_unset_addr_mask(&fc->mask, &fc->mask_mask);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED);
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/access-list/entry/destination-network
+ */
+static int
+lib_access_list_entry_destination_network_create(struct nb_cb_create_args *args)
+{
+ /* Nothing to do here, everything is done in children callbacks */
+ return NB_OK;
+}
+
+static int lib_access_list_entry_destination_network_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct filter_cisco *fc;
+ struct filter *f;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ fc = &f->u.cfilter;
+ fc->extended = 0;
+ cisco_unset_addr_mask(&fc->mask, &fc->mask_mask);
return NB_OK;
}
@@ -955,8 +960,6 @@ static int lib_access_list_entry_destination_network_address_modify(
fc->extended = 1;
yang_dnode_get_ipv4(&fc->mask, args->dnode, NULL);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -988,8 +991,6 @@ static int lib_access_list_entry_destination_network_mask_modify(
fc->extended = 1;
yang_dnode_get_ipv4(&fc->mask_mask, args->dnode, NULL);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -1022,8 +1023,6 @@ static int lib_access_list_entry_destination_any_create(
fc->mask.s_addr = INADDR_ANY;
fc->mask_mask.s_addr = CISCO_BIN_ANY_WILDCARD_MASK;
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -1041,8 +1040,6 @@ static int lib_access_list_entry_destination_any_destroy(
fc->extended = 0;
cisco_unset_addr_mask(&fc->mask, &fc->mask_mask);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED);
-
return NB_OK;
}
@@ -1089,8 +1086,6 @@ static int lib_access_list_entry_any_create(struct nb_cb_create_args *args)
break;
}
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -1106,8 +1101,6 @@ static int lib_access_list_entry_any_destroy(struct nb_cb_destroy_args *args)
fz = &f->u.zfilter;
fz->prefix.family = AF_UNSPEC;
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED);
-
return NB_OK;
}
@@ -1123,8 +1116,8 @@ static int lib_prefix_list_create(struct nb_cb_create_args *args)
if (args->event != NB_EV_APPLY)
return NB_OK;
- type = yang_dnode_get_enum(args->dnode, "./type");
- name = yang_dnode_get_string(args->dnode, "./name");
+ type = yang_dnode_get_enum(args->dnode, "type");
+ name = yang_dnode_get_string(args->dnode, "name");
switch (type) {
case 0: /* ipv4 */
pl = prefix_list_get(AFI_IP, 0, name);
@@ -1201,7 +1194,7 @@ static int lib_prefix_list_entry_create(struct nb_cb_create_args *args)
pl = nb_running_get_entry(args->dnode, NULL, true);
ple = prefix_list_entry_new();
ple->pl = pl;
- ple->seq = yang_dnode_get_uint32(args->dnode, "./sequence");
+ ple->seq = yang_dnode_get_uint32(args->dnode, "sequence");
prefix_list_entry_set_empty(ple);
nb_running_set_entry(args->dnode, ple);
@@ -1224,6 +1217,22 @@ static int lib_prefix_list_entry_destroy(struct nb_cb_destroy_args *args)
return NB_OK;
}
+static void
+lib_prefix_list_entry_apply_finish(struct nb_cb_apply_finish_args *args)
+{
+ struct prefix_list_entry *ple;
+
+ ple = nb_running_get_entry(args->dnode, NULL, true);
+
+ /*
+ * Finish prefix entry update procedure. The procedure is started in
+ * children callbacks. `prefix_list_entry_update_start` can be called
+ * multiple times if multiple children are modified, but it is actually
+ * executed only once because of the protection by `ple->installed`.
+ */
+ prefix_list_entry_update_finish(ple);
+}
+
/*
* XPath: /frr-filter:lib/prefix-list/entry/action
*/
@@ -1246,9 +1255,6 @@ static int lib_prefix_list_entry_action_modify(struct nb_cb_modify_args *args)
else
ple->type = PREFIX_DENY;
- /* Finish prefix entry update procedure. */
- prefix_list_entry_update_finish(ple);
-
return NB_OK;
}
@@ -1276,10 +1282,6 @@ static int lib_prefix_list_entry_prefix_modify(struct nb_cb_modify_args *args)
prefix_copy(&ple->prefix, &p);
}
-
- /* Finish prefix entry update procedure. */
- prefix_list_entry_update_finish(ple);
-
return NB_OK;
}
@@ -1297,9 +1299,6 @@ static int lib_prefix_list_entry_prefix_destroy(struct nb_cb_destroy_args *args)
memset(&ple->prefix, 0, sizeof(ple->prefix));
- /* Finish prefix entry update procedure. */
- prefix_list_entry_update_finish(ple);
-
return NB_OK;
}
@@ -1547,9 +1546,6 @@ static int lib_prefix_list_entry_any_create(struct nb_cb_create_args *args)
break;
}
- /* Finish prefix entry update procedure. */
- prefix_list_entry_update_finish(ple);
-
return NB_OK;
}
@@ -1567,9 +1563,6 @@ static int lib_prefix_list_entry_any_destroy(struct nb_cb_destroy_args *args)
ple->any = false;
- /* Finish prefix entry update procedure. */
- prefix_list_entry_update_finish(ple);
-
return NB_OK;
}
@@ -1597,6 +1590,7 @@ const struct frr_yang_module_info frr_filter_info = {
.cbs = {
.create = lib_access_list_entry_create,
.destroy = lib_access_list_entry_destroy,
+ .apply_finish = lib_access_list_entry_apply_finish,
.cli_cmp = access_list_cmp,
.cli_show = access_list_show,
}
@@ -1629,6 +1623,13 @@ const struct frr_yang_module_info frr_filter_info = {
}
},
{
+ .xpath = "/frr-filter:lib/access-list/entry/network",
+ .cbs = {
+ .create = lib_access_list_entry_network_create,
+ .destroy = lib_access_list_entry_network_destroy,
+ }
+ },
+ {
.xpath = "/frr-filter:lib/access-list/entry/network/address",
.cbs = {
.modify = lib_access_list_entry_network_address_modify,
@@ -1655,6 +1656,13 @@ const struct frr_yang_module_info frr_filter_info = {
}
},
{
+ .xpath = "/frr-filter:lib/access-list/entry/destination-network",
+ .cbs = {
+ .create = lib_access_list_entry_destination_network_create,
+ .destroy = lib_access_list_entry_destination_network_destroy,
+ }
+ },
+ {
.xpath = "/frr-filter:lib/access-list/entry/destination-network/address",
.cbs = {
.modify = lib_access_list_entry_destination_network_address_modify,
@@ -1721,6 +1729,7 @@ const struct frr_yang_module_info frr_filter_info = {
.cbs = {
.create = lib_prefix_list_entry_create,
.destroy = lib_prefix_list_entry_destroy,
+ .apply_finish = lib_prefix_list_entry_apply_finish,
.cli_cmp = prefix_list_cmp,
.cli_show = prefix_list_show,
}
@@ -1785,3 +1794,35 @@ const struct frr_yang_module_info frr_filter_info = {
},
}
};
+
+const struct frr_yang_module_info frr_filter_cli_info = {
+ .name = "frr-filter",
+ .ignore_cfg_cbs = true,
+ .nodes = {
+ {
+ .xpath = "/frr-filter:lib/access-list/remark",
+ .cbs.cli_show = access_list_remark_show,
+ },
+ {
+ .xpath = "/frr-filter:lib/access-list/entry",
+ .cbs = {
+ .cli_cmp = access_list_cmp,
+ .cli_show = access_list_show,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/prefix-list/remark",
+ .cbs.cli_show = prefix_list_remark_show,
+ },
+ {
+ .xpath = "/frr-filter:lib/prefix-list/entry",
+ .cbs = {
+ .cli_cmp = prefix_list_cmp,
+ .cli_show = prefix_list_show,
+ }
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
diff --git a/lib/flex_algo.c b/lib/flex_algo.c
index bafbf8b779..f48117ff1b 100644
--- a/lib/flex_algo.c
+++ b/lib/flex_algo.c
@@ -17,26 +17,42 @@
#include "flex_algo.h"
-DEFINE_MTYPE_STATIC(LIB, FLEX_ALGO, "Flex-Algo Definition");
+DEFINE_MTYPE_STATIC(LIB, FLEX_ALGO_DATABASE, "Flex-Algo database");
+DEFINE_MTYPE_STATIC(LIB, FLEX_ALGO, "Flex-Algo algorithm information");
+
+static void _flex_algo_delete(struct flex_algos *flex_algos,
+ struct flex_algo *fa);
struct flex_algos *flex_algos_alloc(flex_algo_allocator_t allocator,
flex_algo_releaser_t releaser)
{
struct flex_algos *flex_algos;
- flex_algos = XCALLOC(MTYPE_FLEX_ALGO, sizeof(*flex_algos));
+ flex_algos =
+ XCALLOC(MTYPE_FLEX_ALGO_DATABASE, sizeof(struct flex_algos));
flex_algos->flex_algos = list_new();
flex_algos->allocator = allocator;
flex_algos->releaser = releaser;
return flex_algos;
}
+void flex_algos_free(struct flex_algos *flex_algos)
+{
+ struct listnode *node, *nnode;
+ struct flex_algo *fa;
+
+ for (ALL_LIST_ELEMENTS(flex_algos->flex_algos, node, nnode, fa))
+ _flex_algo_delete(flex_algos, fa);
+ list_delete(&flex_algos->flex_algos);
+ XFREE(MTYPE_FLEX_ALGO_DATABASE, flex_algos);
+}
+
struct flex_algo *flex_algo_alloc(struct flex_algos *flex_algos,
uint8_t algorithm, void *arg)
{
struct flex_algo *fa;
- fa = XCALLOC(MTYPE_FLEX_ALGO, sizeof(*fa));
+ fa = XCALLOC(MTYPE_FLEX_ALGO, sizeof(struct flex_algo));
fa->algorithm = algorithm;
if (flex_algos->allocator)
fa->data = flex_algos->allocator(arg);
@@ -47,6 +63,31 @@ struct flex_algo *flex_algo_alloc(struct flex_algos *flex_algos,
return fa;
}
+static void _flex_algo_delete(struct flex_algos *flex_algos,
+ struct flex_algo *fa)
+{
+ if (flex_algos->releaser)
+ flex_algos->releaser(fa->data);
+ admin_group_term(&fa->admin_group_exclude_any);
+ admin_group_term(&fa->admin_group_include_any);
+ admin_group_term(&fa->admin_group_include_all);
+ listnode_delete(flex_algos->flex_algos, fa);
+ XFREE(MTYPE_FLEX_ALGO, fa);
+}
+
+
+void flex_algo_delete(struct flex_algos *flex_algos, uint8_t algorithm)
+{
+ struct listnode *node, *nnode;
+ struct flex_algo *fa;
+
+ for (ALL_LIST_ELEMENTS(flex_algos->flex_algos, node, nnode, fa)) {
+ if (fa->algorithm != algorithm)
+ continue;
+ _flex_algo_delete(flex_algos, fa);
+ }
+}
+
/**
* @brief Look up the local flex-algo object by its algorithm number.
* @param algorithm flex-algo algorithm number
@@ -79,6 +120,12 @@ bool flex_algo_definition_cmp(struct flex_algo *fa1, struct flex_algo *fa2)
return false;
if (fa1->metric_type != fa2->metric_type)
return false;
+ if (fa1->exclude_srlg != fa2->exclude_srlg)
+ return false;
+ if (fa1->flags != fa2->flags)
+ return false;
+ if (fa1->unsupported_subtlv != fa2->unsupported_subtlv)
+ return false;
if (!admin_group_cmp(&fa1->admin_group_exclude_any,
&fa2->admin_group_exclude_any))
@@ -93,25 +140,6 @@ bool flex_algo_definition_cmp(struct flex_algo *fa1, struct flex_algo *fa2)
return true;
}
-void flex_algo_delete(struct flex_algos *flex_algos, uint8_t algorithm)
-{
- struct listnode *node, *nnode;
- struct flex_algo *fa;
-
- for (ALL_LIST_ELEMENTS(flex_algos->flex_algos, node, nnode, fa)) {
- if (fa->algorithm != algorithm)
- continue;
- if (flex_algos->releaser)
- flex_algos->releaser(fa->data);
- admin_group_term(&fa->admin_group_exclude_any);
- admin_group_term(&fa->admin_group_include_any);
- admin_group_term(&fa->admin_group_include_all);
- listnode_delete(flex_algos->flex_algos, fa);
- XFREE(MTYPE_FLEX_ALGO, fa);
- return;
- }
-}
-
/**
* Check SR Algorithm is Flex-Algo
* according to RFC9350 section 4
@@ -140,3 +168,24 @@ char *flex_algo_metric_type_print(char *type_str, size_t sz,
}
return type_str;
}
+
+bool flex_algo_get_state(struct flex_algos *flex_algos, uint8_t algorithm)
+{
+ struct flex_algo *fa = flex_algo_lookup(flex_algos, algorithm);
+
+ if (!fa)
+ return false;
+
+ return fa->state;
+}
+
+void flex_algo_set_state(struct flex_algos *flex_algos, uint8_t algorithm,
+ bool state)
+{
+ struct flex_algo *fa = flex_algo_lookup(flex_algos, algorithm);
+
+ if (!fa)
+ return;
+
+ fa->state = state;
+}
diff --git a/lib/flex_algo.h b/lib/flex_algo.h
index e012f46862..e617e7cae8 100644
--- a/lib/flex_algo.h
+++ b/lib/flex_algo.h
@@ -83,6 +83,11 @@ struct flex_algo {
#define FLEX_ALGO_IP 0x04
uint8_t dataplanes;
+ /* True if the Algorithm is locally enabled (ie. a definition has been
+ * found and is supported).
+ */
+ bool state;
+
/*
* This property can be freely extended among different routing
* protocols. Since Flex-Algo is an IGP protocol agnostic, both IS-IS
@@ -107,6 +112,7 @@ struct flex_algos {
*/
struct flex_algos *flex_algos_alloc(flex_algo_allocator_t allocator,
flex_algo_releaser_t releaser);
+void flex_algos_free(struct flex_algos *flex_algos);
struct flex_algo *flex_algo_alloc(struct flex_algos *flex_algos,
uint8_t algorithm, void *arg);
struct flex_algo *flex_algo_lookup(struct flex_algos *flex_algos,
@@ -118,4 +124,8 @@ bool flex_algo_id_valid(uint16_t algorithm);
char *flex_algo_metric_type_print(char *type_str, size_t sz,
enum flex_algo_metric_type metric_type);
+bool flex_algo_get_state(struct flex_algos *flex_algos, uint8_t algorithm);
+
+void flex_algo_set_state(struct flex_algos *flex_algos, uint8_t algorithm,
+ bool state);
#endif /* _FRR_FLEX_ALGO_H */
diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c
index c4ead01bf6..1ffa5934aa 100644
--- a/lib/frr_pthread.c
+++ b/lib/frr_pthread.c
@@ -5,6 +5,9 @@
*/
#include <zebra.h>
+
+#include <signal.h>
+
#include <pthread.h>
#ifdef HAVE_PTHREAD_NP_H
#include <pthread_np.h>
@@ -217,6 +220,41 @@ void frr_pthread_stop_all(void)
}
}
+static void *frr_pthread_attr_non_controlled_start(void *arg)
+{
+ struct frr_pthread *fpt = arg;
+
+ fpt->running = true;
+
+ return NULL;
+}
+
+/* Create a FRR pthread context from a non FRR pthread initialized from an
+ * external library in order to allow logging */
+int frr_pthread_non_controlled_startup(pthread_t thread, const char *name,
+ const char *os_name)
+{
+ struct rcu_thread *rcu_thread = rcu_thread_new(NULL);
+
+ rcu_thread_start(rcu_thread);
+
+ struct frr_pthread_attr attr = {
+ .start = frr_pthread_attr_non_controlled_start,
+ .stop = frr_pthread_attr_default.stop,
+ };
+ struct frr_pthread *fpt;
+
+ fpt = frr_pthread_new(&attr, name, os_name);
+ if (!fpt)
+ return -1;
+
+ fpt->thread = thread;
+ fpt->rcu_thread = rcu_thread;
+ frr_pthread_inner(fpt);
+
+ return 0;
+}
+
/*
* ----------------------------------------------------------------------------
* Default Event Loop
diff --git a/lib/frr_pthread.h b/lib/frr_pthread.h
index f91044dfae..1e1b8d7fd3 100644
--- a/lib/frr_pthread.h
+++ b/lib/frr_pthread.h
@@ -202,6 +202,9 @@ void frr_pthread_stop_all(void);
#define pthread_condattr_setclock(A, B)
#endif
+int frr_pthread_non_controlled_startup(pthread_t thread, const char *name,
+ const char *os_name);
+
/* mutex auto-lock/unlock */
/* variant 1:
diff --git a/lib/frrcu.c b/lib/frrcu.c
index c7cc655e09..b85c525c58 100644
--- a/lib/frrcu.c
+++ b/lib/frrcu.c
@@ -149,20 +149,9 @@ static struct rcu_thread *rcu_self(void)
return (struct rcu_thread *)pthread_getspecific(rcu_thread_key);
}
-/*
- * thread management (for the non-main thread)
- */
-struct rcu_thread *rcu_thread_prepare(void)
+struct rcu_thread *rcu_thread_new(void *arg)
{
- struct rcu_thread *rt, *cur;
-
- rcu_assert_read_locked();
-
- if (!rcu_active)
- rcu_start();
-
- cur = rcu_self();
- assert(cur->depth);
+ struct rcu_thread *rt, *cur = arg;
/* new thread always starts with rcu_read_lock held at depth 1, and
* holding the same epoch as the parent (this makes it possible to
@@ -172,13 +161,32 @@ struct rcu_thread *rcu_thread_prepare(void)
rt->depth = 1;
seqlock_init(&rt->rcu);
- seqlock_acquire(&rt->rcu, &cur->rcu);
+ if (cur)
+ seqlock_acquire(&rt->rcu, &cur->rcu);
rcu_threads_add_tail(&rcu_threads, rt);
return rt;
}
+/*
+ * thread management (for the non-main thread)
+ */
+struct rcu_thread *rcu_thread_prepare(void)
+{
+ struct rcu_thread *cur;
+
+ rcu_assert_read_locked();
+
+ if (!rcu_active)
+ rcu_start();
+
+ cur = rcu_self();
+ assert(cur->depth);
+
+ return rcu_thread_new(cur);
+}
+
void rcu_thread_start(struct rcu_thread *rt)
{
pthread_setspecific(rcu_thread_key, rt);
diff --git a/lib/frrcu.h b/lib/frrcu.h
index e7a54dcbe5..9f07a69b52 100644
--- a/lib/frrcu.h
+++ b/lib/frrcu.h
@@ -40,6 +40,12 @@ extern "C" {
/* opaque */
struct rcu_thread;
+/* sets up rcu thread info
+ *
+ * return value must be passed into the thread's call to rcu_thread_start()
+ */
+extern struct rcu_thread *rcu_thread_new(void *arg);
+
/* called before new thread creation, sets up rcu thread info for new thread
* before it actually exits. This ensures possible RCU references are held
* for thread startup.
diff --git a/lib/frrdistance.h b/lib/frrdistance.h
new file mode 100644
index 0000000000..d2fa76e610
--- /dev/null
+++ b/lib/frrdistance.h
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Zebra distance header
+ * Copyright (C) 2023 NVIDIA Corporation
+ * Donald Sharp
+ *
+ * Distance related defines. FRR needs a common set
+ * of values for distance.
+ */
+#ifndef __FRRDISTANCE_H__
+#define __FRRDISTANCE_H__
+
+/* Default Administrative Distance of each protocol. */
+#define ZEBRA_KERNEL_DISTANCE_DEFAULT 0
+#define ZEBRA_CONNECT_DISTANCE_DEFAULT 0
+#define ZEBRA_STATIC_DISTANCE_DEFAULT 1
+#define ZEBRA_RIP_DISTANCE_DEFAULT 120
+#define ZEBRA_RIPNG_DISTANCE_DEFAULT 120
+#define ZEBRA_OSPF_DISTANCE_DEFAULT 110
+#define ZEBRA_OSPF6_DISTANCE_DEFAULT 110
+#define ZEBRA_ISIS_DISTANCE_DEFAULT 115
+#define ZEBRA_IBGP_DISTANCE_DEFAULT 200
+#define ZEBRA_EBGP_DISTANCE_DEFAULT 20
+#define ZEBRA_TABLE_DISTANCE_DEFAULT 15
+#define ZEBRA_TABLEDIRECT_DISTANCE_DEFAULT 14
+#define ZEBRA_EIGRP_DISTANCE_DEFAULT 90
+#define ZEBRA_NHRP_DISTANCE_DEFAULT 10
+#define ZEBRA_LDP_DISTANCE_DEFAULT 150
+#define ZEBRA_BABEL_DISTANCE_DEFAULT 100
+#define ZEBRA_SHARP_DISTANCE_DEFAULT 150
+#define ZEBRA_PBR_DISTANCE_DEFAULT 200
+#define ZEBRA_OPENFABRIC_DISTANCE_DEFAULT 115
+#define ZEBRA_MAX_DISTANCE_DEFAULT 255
+
+#endif
diff --git a/lib/frrevent.h b/lib/frrevent.h
index 2b0c52bb51..94640a76b7 100644
--- a/lib/frrevent.h
+++ b/lib/frrevent.h
@@ -6,6 +6,7 @@
#ifndef _ZEBRA_THREAD_H
#define _ZEBRA_THREAD_H
+#include <signal.h>
#include <zebra.h>
#include <pthread.h>
#include <poll.h>
@@ -18,6 +19,8 @@
extern "C" {
#endif
+#define CONSUMED_TIME_CHECK 5000000
+
extern bool cputime_enabled;
extern unsigned long cputime_threshold;
/* capturing wallclock time is always enabled since it is fast (reading
@@ -65,6 +68,8 @@ struct xref_eventsched {
uint32_t event_type;
};
+PREDECL_HASH(cpu_records);
+
/* Master of the theads. */
struct event_loop {
char *name;
@@ -76,7 +81,7 @@ struct event_loop {
struct list *cancel_req;
bool canceled;
pthread_cond_t cancel_cond;
- struct hash *cpu_record;
+ struct cpu_records_head cpu_records[1];
int io_pipe[2];
int fd_limit;
struct fd_handler handler;
@@ -87,6 +92,8 @@ struct event_loop {
pthread_mutex_t mtx;
pthread_t owner;
+ nfds_t last_read;
+
bool ready_run_loop;
RUSAGE_T last_getrusage;
};
@@ -130,6 +137,8 @@ struct event {
#endif
struct cpu_event_history {
+ struct cpu_records_item item;
+
void (*func)(struct event *e);
atomic_size_t total_cpu_warn;
atomic_size_t total_wall_warn;
@@ -147,6 +156,12 @@ struct cpu_event_history {
/* Struct timeval's tv_usec one second value. */
#define TIMER_SECOND_MICRO 1000000L
+static inline unsigned long timeval_elapsed(struct timeval a, struct timeval b)
+{
+ return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO)
+ + (a.tv_usec - b.tv_usec));
+}
+
/* Event yield time. */
#define EVENT_YIELD_TIME_SLOT 10 * 1000L /* 10ms */
@@ -195,7 +210,7 @@ struct cpu_event_history {
_xref_t_a(timer_tv, TIMER, m, f, a, v, t)
#define event_add_event(m, f, a, v, t) _xref_t_a(event, EVENT, m, f, a, v, t)
-#define event_execute(m, f, a, v) \
+#define event_execute(m, f, a, v, p) \
({ \
static const struct xref_eventsched _xref __attribute__( \
(used)) = { \
@@ -205,7 +220,7 @@ struct cpu_event_history {
.event_type = EVENT_EXECUTE, \
}; \
XREF_LINK(_xref.xref); \
- _event_execute(&_xref, m, f, a, v); \
+ _event_execute(&_xref, m, f, a, v, p); \
}) /* end */
/* Prototypes. */
@@ -241,7 +256,8 @@ extern void _event_add_event(const struct xref_eventsched *xref,
extern void _event_execute(const struct xref_eventsched *xref,
struct event_loop *master,
- void (*fn)(struct event *), void *arg, int val);
+ void (*fn)(struct event *), void *arg, int val,
+ struct event **eref);
extern void event_cancel(struct event **event);
extern void event_cancel_async(struct event_loop *m, struct event **eptr,
diff --git a/lib/frrlua.c b/lib/frrlua.c
index 5b49d32a9c..2cab1a5460 100644
--- a/lib/frrlua.c
+++ b/lib/frrlua.c
@@ -259,31 +259,12 @@ void *lua_tosockunion(lua_State *L, int idx)
return su;
}
-void lua_pushtimet(lua_State *L, const time_t *time)
-{
- lua_pushinteger(L, *time);
-}
-
-void lua_decode_timet(lua_State *L, int idx, time_t *t)
-{
- *t = lua_tointeger(L, idx);
- lua_pop(L, 1);
-}
-
-void *lua_totimet(lua_State *L, int idx)
-{
- time_t *t = XCALLOC(MTYPE_SCRIPT_RES, sizeof(time_t));
-
- lua_decode_timet(L, idx, t);
- return t;
-}
-
-void lua_pushintegerp(lua_State *L, const long long *num)
+void lua_pushintegerp(lua_State *L, const int *num)
{
lua_pushinteger(L, *num);
}
-void lua_decode_integerp(lua_State *L, int idx, long long *num)
+void lua_decode_integerp(lua_State *L, int idx, int *num)
{
int isnum;
*num = lua_tonumberx(L, idx, &isnum);
@@ -293,7 +274,7 @@ void lua_decode_integerp(lua_State *L, int idx, long long *num)
void *lua_tointegerp(lua_State *L, int idx)
{
- long long *num = XCALLOC(MTYPE_SCRIPT_RES, sizeof(long long));
+ int *num = XCALLOC(MTYPE_SCRIPT_RES, sizeof(int));
lua_decode_integerp(L, idx, num);
return num;
@@ -351,32 +332,39 @@ void lua_pushnexthop_group(lua_State *L, const struct nexthop_group *ng)
}
}
-void lua_decode_stringp(lua_State *L, int idx, char *str)
+void lua_pushlonglongp(lua_State *L, const long long *num)
{
- strlcpy(str, lua_tostring(L, idx), strlen(str) + 1);
+ /* lua library function; this can take a long long */
+ lua_pushinteger(L, *num);
+}
+
+void lua_decode_longlongp(lua_State *L, int idx, long long *num)
+{
+ int isnum;
+ *num = lua_tonumberx(L, idx, &isnum);
lua_pop(L, 1);
+ assert(isnum);
}
-void *lua_tostringp(lua_State *L, int idx)
+void *lua_tolonglongp(lua_State *L, int idx)
{
- char *string = XSTRDUP(MTYPE_SCRIPT_RES, lua_tostring(L, idx));
+ long long *num = XCALLOC(MTYPE_SCRIPT_RES, sizeof(long long));
- return string;
+ lua_decode_longlongp(L, idx, num);
+ return num;
}
-/*
- * Decoder for const values, since we cannot modify them.
- */
-void lua_decode_noop(lua_State *L, int idx, const void *ptr)
+void lua_decode_stringp(lua_State *L, int idx, char *str)
{
+ strlcpy(str, lua_tostring(L, idx), strlen(str) + 1);
+ lua_pop(L, 1);
}
-
-/*
- * Noop decoder for int.
- */
-void lua_decode_integer_noop(lua_State *L, int idx, int i)
+void *lua_tostringp(lua_State *L, int idx)
{
+ char *string = XSTRDUP(MTYPE_SCRIPT_RES, lua_tostring(L, idx));
+
+ return string;
}
/*
diff --git a/lib/frrlua.h b/lib/frrlua.h
index bf6eb5fd91..dc0f4d9986 100644
--- a/lib/frrlua.h
+++ b/lib/frrlua.h
@@ -100,21 +100,6 @@ void lua_pushethaddr(lua_State *L, const struct ethaddr *addr);
void *lua_toin6addr(lua_State *L, int idx);
/*
- * Converts a time_t to a Lua value and pushes it on the stack.
- */
-void lua_pushtimet(lua_State *L, const time_t *time);
-
-void lua_decode_timet(lua_State *L, int idx, time_t *time);
-
-/*
- * Converts the Lua value at idx to a time_t.
- *
- * Returns:
- * time_t allocated with MTYPE_TMP.
- */
-void *lua_totimet(lua_State *L, int idx);
-
-/*
* Converts a sockunion to a Lua value and pushes it on the stack.
*/
void lua_pushsockunion(lua_State *L, const union sockunion *su);
@@ -136,9 +121,9 @@ void lua_pushnexthop(lua_State *L, const struct nexthop *nexthop);
/*
* Converts an int to a Lua value and pushes it on the stack.
*/
-void lua_pushintegerp(lua_State *L, const long long *num);
+void lua_pushintegerp(lua_State *L, const int *num);
-void lua_decode_integerp(lua_State *L, int idx, long long *num);
+void lua_decode_integerp(lua_State *L, int idx, int *num);
/*
* Converts the Lua value at idx to an int.
@@ -148,6 +133,21 @@ void lua_decode_integerp(lua_State *L, int idx, long long *num);
*/
void *lua_tointegerp(lua_State *L, int idx);
+/*
+ * Converts a long long to a Lua value and pushes it on the stack.
+ */
+void lua_pushlonglongp(lua_State *L, const long long *num);
+
+void lua_decode_longlongp(lua_State *L, int idx, long long *num);
+
+/*
+ * Converts the Lua value at idx to a long long.
+ *
+ * Returns:
+ * long long allocated with MTYPE_TMP.
+ */
+void *lua_tolonglongp(lua_State *L, int idx);
+
void lua_decode_stringp(lua_State *L, int idx, char *str);
/*
@@ -159,13 +159,6 @@ void lua_decode_stringp(lua_State *L, int idx, char *str);
void *lua_tostringp(lua_State *L, int idx);
/*
- * No-op decoders
- */
-void lua_decode_noop(lua_State *L, int idx, const void *ptr);
-
-void lua_decode_integer_noop(lua_State *L, int idx, int i);
-
-/*
* Retrieve an integer from table on the top of the stack.
*
* key
diff --git a/lib/frrscript.c b/lib/frrscript.c
index 1b99c7e2c6..acdd1df67b 100644
--- a/lib/frrscript.c
+++ b/lib/frrscript.c
@@ -25,6 +25,8 @@ DEFINE_MTYPE_STATIC(LIB, SCRIPT, "Scripting");
struct frrscript_names_head frrscript_names_hash;
+void _lua_decode_noop(lua_State *L, ...) {}
+
/*
* Wrapper for frrscript_names_add
* Use this to register hook calls when a daemon starts up
@@ -133,9 +135,6 @@ struct frrscript_codec frrscript_codecs_lib[] = {
{.typename = "sockunion",
.encoder = (encoder_func)lua_pushsockunion,
.decoder = lua_tosockunion},
- {.typename = "time_t",
- .encoder = (encoder_func)lua_pushtimet,
- .decoder = lua_totimet},
{}};
/* Type codecs */
diff --git a/lib/frrscript.h b/lib/frrscript.h
index 7ba4f0cbdb..ce313a1b63 100644
--- a/lib/frrscript.h
+++ b/lib/frrscript.h
@@ -181,6 +181,11 @@ void frrscript_fini(void);
} while (0)
/*
+ * Noop function. Used below where we need a noop decoder for any type.
+ */
+void _lua_decode_noop(lua_State *, ...);
+
+/*
* Maps the type of value to its encoder/decoder.
* Add new mappings here.
*
@@ -192,13 +197,14 @@ void frrscript_fini(void);
#define ENCODE_ARGS_WITH_STATE(L, value) \
_Generic((value), \
int : lua_pushinteger, \
-long long * : lua_pushintegerp, \
+int * : lua_pushintegerp, \
+long long : lua_pushinteger, \
+long long * : lua_pushlonglongp, \
struct prefix * : lua_pushprefix, \
struct interface * : lua_pushinterface, \
struct in_addr * : lua_pushinaddr, \
struct in6_addr * : lua_pushin6addr, \
union sockunion * : lua_pushsockunion, \
-time_t * : lua_pushtimet, \
char * : lua_pushstring_wrapper, \
struct attr * : lua_pushattr, \
struct peer * : lua_pushpeer, \
@@ -212,23 +218,16 @@ struct zebra_dplane_ctx * : lua_pushzebra_dplane_ctx \
#define DECODE_ARGS_WITH_STATE(L, value) \
_Generic((value), \
-int : lua_decode_integer_noop, \
-long long * : lua_decode_integerp, \
+int * : lua_decode_integerp, \
+long long * : lua_decode_longlongp, \
struct prefix * : lua_decode_prefix, \
struct interface * : lua_decode_interface, \
struct in_addr * : lua_decode_inaddr, \
struct in6_addr * : lua_decode_in6addr, \
union sockunion * : lua_decode_sockunion, \
-time_t * : lua_decode_timet, \
char * : lua_decode_stringp, \
struct attr * : lua_decode_attr, \
-struct peer * : lua_decode_noop, \
-const struct prefix * : lua_decode_noop, \
-const struct ipaddr * : lua_decode_noop, \
-const struct ethaddr * : lua_decode_noop, \
-const struct nexthop_group * : lua_decode_noop, \
-const struct nexthop * : lua_decode_noop, \
-struct zebra_dplane_ctx * : lua_decode_noop \
+default : _lua_decode_noop \
)((L), -1, (value))
/*
diff --git a/lib/frrsendmmsg.h b/lib/frrsendmmsg.h
new file mode 100644
index 0000000000..ea63d139aa
--- /dev/null
+++ b/lib/frrsendmmsg.h
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * FRR sendmmsg wrapper
+ * Copyright (C) 2024 by Nvidia, Inc.
+ * Donald Sharp
+ */
+#ifndef __FRRSENDMMSG_H__
+#define __FRRSENDMMSG_H__
+
+#if !defined(HAVE_STRUCT_MMSGHDR_MSG_HDR) || !defined(HAVE_SENDMMSG)
+/* avoid conflicts in case we have partial support */
+#define mmsghdr frr_mmsghdr
+#define sendmmsg frr_sendmmsg
+
+struct mmsghdr {
+ struct msghdr msg_hdr;
+ unsigned int msg_len;
+};
+
+/* just go 1 at a time here, the loop this is used in will handle the rest */
+static inline int sendmmsg(int fd, struct mmsghdr *mmh, unsigned int len,
+ int flags)
+{
+ int rv = sendmsg(fd, &mmh->msg_hdr, 0);
+
+ return rv > 0 ? 1 : rv;
+}
+#endif
+
+#endif
diff --git a/lib/frrstr.c b/lib/frrstr.c
index e5440c5093..1e743d4b0c 100644
--- a/lib/frrstr.c
+++ b/lib/frrstr.c
@@ -3,6 +3,7 @@
* FRR string processing utilities.
* Copyright (C) 2018 Cumulus Networks, Inc.
* Quentin Young
+ * Copyright (c) 2023, LabN Consulting, L.L.C.
*/
#include "zebra.h"
@@ -225,3 +226,47 @@ char *frrstr_hex(char *buff, size_t bufsiz, const uint8_t *str, size_t num)
return buff;
}
+
+const char *frrstr_skip_over_char(const char *s, int skipc)
+{
+ int c, quote = 0;
+
+ while ((c = *s++)) {
+ if (c == '\\') {
+ if (!*s++)
+ return NULL;
+ continue;
+ }
+ if (quote) {
+ if (c == quote)
+ quote = 0;
+ continue;
+ }
+ if (c == skipc)
+ return s;
+ if (c == '"' || c == '\'')
+ quote = c;
+ }
+ return NULL;
+}
+
+/*
+ * Advance backward in string until reaching the char `toc`
+ * if beginning of string is reached w/o finding char return NULL
+ *
+ * /foo/bar'baz/booz'/foo
+ */
+const char *frrstr_back_to_char(const char *s, int toc)
+{
+ const char *next = s;
+ const char *prev = NULL;
+
+ if (s[0] == 0)
+ return NULL;
+ if (!strpbrk(s, "'\"\\"))
+ return strrchr(s, toc);
+ while ((next = frrstr_skip_over_char(next, toc)))
+ prev = next - 1;
+ return prev;
+}
+
diff --git a/lib/frrstr.h b/lib/frrstr.h
index 19ba09e213..33a4992001 100644
--- a/lib/frrstr.h
+++ b/lib/frrstr.h
@@ -3,6 +3,7 @@
* FRR string processing utilities.
* Copyright (C) 2018 Cumulus Networks, Inc.
* Quentin Young
+ * Copyright (c) 2023, LabN Consulting, L.L.C.
*/
#ifndef _FRRSTR_H_
@@ -166,6 +167,19 @@ int all_digit(const char *str);
*/
char *frrstr_hex(char *buff, size_t bufsiz, const uint8_t *str, size_t num);
+/*
+ * Advance past a given char `skipc` in a string, while honoring quoting and
+ * backslash escapes (i.e., ignore `skipc` which occur in quoted sections).
+ */
+const char *frrstr_skip_over_char(const char *s, int skipc);
+
+/*
+ * Advance back from end to a given char `toc` in a string, while honoring
+ * quoting and backslash escapes. `toc` chars inside quote or escaped are
+ * ignored.
+ */
+const char *frrstr_back_to_char(const char *s, int toc);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c
index fc7ebebbe5..24833e08ec 100644
--- a/lib/grammar_sandbox.c
+++ b/lib/grammar_sandbox.c
@@ -19,7 +19,7 @@
#define GRAMMAR_STR "CLI grammar sandbox\n"
-DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc");
+DEFINE_MTYPE_STATIC(LIB, CMD_DESCRIPTIONS, "Command desc");
/** headers **/
void grammar_sandbox_init(void);
@@ -53,7 +53,7 @@ DEFUN (grammar_test,
// create cmd_element for parser
struct cmd_element *cmd =
- XCALLOC(MTYPE_CMD_TOKENS, sizeof(struct cmd_element));
+ XCALLOC(MTYPE_CMD_DESCRIPTIONS, sizeof(struct cmd_element));
cmd->string = command;
cmd->doc =
"0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n";
@@ -207,11 +207,11 @@ DEFUN (grammar_test_doc,
// create cmd_element with docstring
struct cmd_element *cmd =
- XCALLOC(MTYPE_CMD_TOKENS, sizeof(struct cmd_element));
+ XCALLOC(MTYPE_CMD_DESCRIPTIONS, sizeof(struct cmd_element));
cmd->string = XSTRDUP(
- MTYPE_CMD_TOKENS,
+ MTYPE_CMD_DESCRIPTIONS,
"test docstring <example|selector follow> (1-255) end VARIABLE [OPTION|set lol] . VARARG");
- cmd->doc = XSTRDUP(MTYPE_CMD_TOKENS,
+ cmd->doc = XSTRDUP(MTYPE_CMD_DESCRIPTIONS,
"Test stuff\n"
"docstring thing\n"
"first example\n"
diff --git a/lib/hash.h b/lib/hash.h
index 810faf9030..2d00a334be 100644
--- a/lib/hash.h
+++ b/lib/hash.h
@@ -28,12 +28,12 @@ struct hash_bucket {
*/
int len;
- /* Linked list. */
- struct hash_bucket *next;
-
/* Hash key. */
unsigned int key;
+ /* Linked list. */
+ struct hash_bucket *next;
+
/* Data. */
void *data;
};
diff --git a/lib/hook.h b/lib/hook.h
index 19e0f1fbfc..58aa2009b9 100644
--- a/lib/hook.h
+++ b/lib/hook.h
@@ -163,6 +163,8 @@ extern void _hook_unregister(struct hook *hook, void *funcptr, void *arg,
_hook_unregister(&_hook_##hookname, \
_hook_typecheck_arg_##hookname(func), arg, true)
+#define hook_have_hooks(hookname) (_hook_##hookname.entries != NULL)
+
/* invoke hooks
* this is private (static) to the file that has the DEFINE_HOOK statement
*/
diff --git a/lib/if.c b/lib/if.c
index 08d8918742..a8ceac7243 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -42,15 +42,14 @@ RB_GENERATE(if_index_head, interface, index_entry, if_cmp_index_func);
DEFINE_QOBJ_TYPE(interface);
-DEFINE_HOOK(if_add, (struct interface * ifp), (ifp));
-DEFINE_KOOH(if_del, (struct interface * ifp), (ifp));
+DEFINE_HOOK(if_add, (struct interface *ifp), (ifp));
+DEFINE_KOOH(if_del, (struct interface *ifp), (ifp));
-static struct interface_master{
- int (*create_hook)(struct interface *ifp);
- int (*up_hook)(struct interface *ifp);
- int (*down_hook)(struct interface *ifp);
- int (*destroy_hook)(struct interface *ifp);
-} ifp_master = { 0, };
+DEFINE_HOOK(if_real, (struct interface *ifp), (ifp));
+DEFINE_KOOH(if_unreal, (struct interface *ifp), (ifp));
+
+DEFINE_HOOK(if_up, (struct interface *ifp), (ifp));
+DEFINE_KOOH(if_down, (struct interface *ifp), (ifp));
/* Compare interface names, returning an integer greater than, equal to, or
* less than 0, (following the strcmp convention), according to the
@@ -98,8 +97,8 @@ int if_cmp_name_func(const char *p1, const char *p2)
if (!*p2)
return 1;
- x1 = strtol(p1, (char **)&tmp1, 10);
- x2 = strtol(p2, (char **)&tmp2, 10);
+ x1 = strtol(p1, &tmp1, 10);
+ x2 = strtol(p2, &tmp2, 10);
/* let's compare numbers now */
if (x1 < x2)
@@ -165,8 +164,7 @@ static struct interface *if_new(struct vrf *vrf)
ifp->vrf = vrf;
- ifp->connected = list_new();
- ifp->connected->del = ifp_connected_free;
+ if_connected_init(ifp->connected);
ifp->nbr_connected = list_new();
ifp->nbr_connected->del = (void (*)(void *))nbr_connected_free;
@@ -180,14 +178,12 @@ static struct interface *if_new(struct vrf *vrf)
void if_new_via_zapi(struct interface *ifp)
{
- if (ifp_master.create_hook)
- (*ifp_master.create_hook)(ifp);
+ hook_call(if_real, ifp);
}
void if_destroy_via_zapi(struct interface *ifp)
{
- if (ifp_master.destroy_hook)
- (*ifp_master.destroy_hook)(ifp);
+ hook_call(if_unreal, ifp);
ifp->oldifindex = ifp->ifindex;
if_set_index(ifp, IFINDEX_INTERNAL);
@@ -198,14 +194,12 @@ void if_destroy_via_zapi(struct interface *ifp)
void if_up_via_zapi(struct interface *ifp)
{
- if (ifp_master.up_hook)
- (*ifp_master.up_hook)(ifp);
+ hook_call(if_up, ifp);
}
void if_down_via_zapi(struct interface *ifp)
{
- if (ifp_master.down_hook)
- (*ifp_master.down_hook)(ifp);
+ hook_call(if_down, ifp);
}
static struct interface *if_create_name(const char *name, struct vrf *vrf)
@@ -248,11 +242,14 @@ void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id)
/* Delete interface structure. */
void if_delete_retain(struct interface *ifp)
{
+ struct connected *ifc;
+
hook_call(if_del, ifp);
QOBJ_UNREG(ifp);
/* Free connected address list */
- list_delete_all_node(ifp->connected);
+ while ((ifc = if_connected_pop(ifp->connected)))
+ ifp_connected_free(ifc);
/* Free connected nbr address list */
list_delete_all_node(ifp->nbr_connected);
@@ -270,7 +267,7 @@ void if_delete(struct interface **ifp)
if_delete_retain(ptr);
- list_delete(&ptr->connected);
+ if_connected_fini(ptr->connected);
list_delete(&ptr->nbr_connected);
if_link_params_free(ptr);
@@ -367,8 +364,7 @@ struct interface *if_lookup_by_name(const char *name, vrf_id_t vrf_id)
struct vrf *vrf = vrf_lookup_by_id(vrf_id);
struct interface if_tmp;
- if (!vrf || !name
- || strnlen(name, INTERFACE_NAMSIZ) == INTERFACE_NAMSIZ)
+ if (!vrf || !name || strnlen(name, IFNAMSIZ) == IFNAMSIZ)
return NULL;
strlcpy(if_tmp.name, name, sizeof(if_tmp.name));
@@ -379,7 +375,7 @@ struct interface *if_lookup_by_name_vrf(const char *name, struct vrf *vrf)
{
struct interface if_tmp;
- if (!name || strnlen(name, INTERFACE_NAMSIZ) == INTERFACE_NAMSIZ)
+ if (!name || strnlen(name, IFNAMSIZ) == IFNAMSIZ)
return NULL;
strlcpy(if_tmp.name, name, sizeof(if_tmp.name));
@@ -391,7 +387,7 @@ static struct interface *if_lookup_by_name_all_vrf(const char *name)
struct vrf *vrf;
struct interface *ifp;
- if (!name || strnlen(name, INTERFACE_NAMSIZ) == INTERFACE_NAMSIZ)
+ if (!name || strnlen(name, IFNAMSIZ) == IFNAMSIZ)
return NULL;
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
@@ -433,7 +429,6 @@ struct interface *if_lookup_address_local(const void *src, int family,
vrf_id_t vrf_id)
{
struct vrf *vrf = vrf_lookup_by_id(vrf_id);
- struct listnode *cnode;
struct interface *ifp, *best_down = NULL;
struct prefix *p;
struct connected *c;
@@ -442,7 +437,7 @@ struct interface *if_lookup_address_local(const void *src, int family,
return NULL;
FOR_ALL_INTERFACES (vrf, ifp) {
- for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) {
+ frr_each (if_connected, ifp->connected, c) {
p = c->address;
if (!p || p->family != family)
@@ -474,7 +469,6 @@ struct connected *if_lookup_address(const void *matchaddr, int family,
struct vrf *vrf = vrf_lookup_by_id(vrf_id);
struct prefix addr;
int bestlen = 0;
- struct listnode *cnode;
struct interface *ifp;
struct connected *c;
struct connected *match;
@@ -493,7 +487,7 @@ struct connected *if_lookup_address(const void *matchaddr, int family,
match = NULL;
FOR_ALL_INTERFACES (vrf, ifp) {
- for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) {
+ frr_each (if_connected, ifp->connected, c) {
if (c->address && (c->address->family == AF_INET)
&& prefix_match(CONNECTED_PREFIX(c), &addr)
&& (c->address->prefixlen > bestlen)) {
@@ -509,12 +503,11 @@ struct connected *if_lookup_address(const void *matchaddr, int family,
struct interface *if_lookup_prefix(const struct prefix *prefix, vrf_id_t vrf_id)
{
struct vrf *vrf = vrf_lookup_by_id(vrf_id);
- struct listnode *cnode;
struct interface *ifp;
struct connected *c;
FOR_ALL_INTERFACES (vrf, ifp) {
- for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) {
+ frr_each (if_connected, ifp->connected, c) {
if (prefix_cmp(c->address, prefix) == 0) {
return ifp;
}
@@ -781,10 +774,9 @@ const char *if_flag_dump(unsigned long flag)
/* For debugging */
static void if_dump(const struct interface *ifp)
{
- struct listnode *node;
- struct connected *c __attribute__((unused));
+ const struct connected *c;
- for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, c))
+ frr_each (if_connected_const, ifp->connected, c)
zlog_info(
"Interface %s vrf %s(%u) index %d metric %d mtu %d mtu6 %d %s",
ifp->name, ifp->vrf->name, ifp->vrf->vrf_id,
@@ -911,11 +903,10 @@ static int connected_same_prefix(const struct prefix *p1,
/* count the number of connected addresses that are in the given family */
unsigned int connected_count_by_family(struct interface *ifp, int family)
{
- struct listnode *cnode;
struct connected *connected;
unsigned int cnt = 0;
- for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected))
+ frr_each (if_connected, ifp->connected, connected)
if (connected->address->family == family)
cnt++;
@@ -925,14 +916,9 @@ unsigned int connected_count_by_family(struct interface *ifp, int family)
struct connected *connected_lookup_prefix_exact(struct interface *ifp,
const struct prefix *p)
{
- struct listnode *node;
- struct listnode *next;
struct connected *ifc;
- for (node = listhead(ifp->connected); node; node = next) {
- ifc = listgetdata(node);
- next = node->next;
-
+ frr_each (if_connected, ifp->connected, ifc) {
if (connected_same_prefix(ifc->address, p))
return ifc;
}
@@ -942,17 +928,12 @@ struct connected *connected_lookup_prefix_exact(struct interface *ifp,
struct connected *connected_delete_by_prefix(struct interface *ifp,
struct prefix *p)
{
- struct listnode *node;
- struct listnode *next;
struct connected *ifc;
/* In case of same prefix come, replace it with new one. */
- for (node = listhead(ifp->connected); node; node = next) {
- ifc = listgetdata(node);
- next = node->next;
-
+ frr_each_safe (if_connected, ifp->connected, ifc) {
if (connected_same_prefix(ifc->address, p)) {
- listnode_delete(ifp->connected, ifc);
+ if_connected_del(ifp->connected, ifc);
return ifc;
}
}
@@ -964,13 +945,12 @@ struct connected *connected_delete_by_prefix(struct interface *ifp,
struct connected *connected_lookup_prefix(struct interface *ifp,
const struct prefix *addr)
{
- struct listnode *cnode;
struct connected *c;
struct connected *match;
match = NULL;
- for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) {
+ frr_each (if_connected, ifp->connected, c) {
if (c->address && (c->address->family == addr->family)
&& prefix_match(CONNECTED_PREFIX(c), addr)
&& (!match
@@ -1001,16 +981,15 @@ struct connected *connected_add_by_prefix(struct interface *ifp,
}
/* Add connected address to the interface. */
- listnode_add(ifp->connected, ifc);
+ if_connected_add_tail(ifp->connected, ifc);
return ifc;
}
struct connected *connected_get_linklocal(struct interface *ifp)
{
- struct listnode *n;
struct connected *c = NULL;
- for (ALL_LIST_ELEMENTS_RO(ifp->connected, n, c)) {
+ frr_each (if_connected, ifp->connected, c) {
if (c->address->family == AF_INET6
&& IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6))
break;
@@ -1028,6 +1007,7 @@ void if_terminate(struct vrf *vrf)
if (ifp->node) {
ifp->node->info = NULL;
route_unlock_node(ifp->node);
+ ifp->node = NULL;
}
if_delete(&ifp);
}
@@ -1339,14 +1319,14 @@ static void cli_show_interface(struct vty *vty, const struct lyd_node *dnode,
char ifname[XPATH_MAXLEN];
char vrfname[XPATH_MAXLEN];
- netns_ifname_split(yang_dnode_get_string(dnode, "./name"),
+ netns_ifname_split(yang_dnode_get_string(dnode, "name"),
ifname, vrfname);
vty_out(vty, "interface %s", ifname);
if (!strmatch(vrfname, VRF_DEFAULT_NAME))
vty_out(vty, " vrf %s", vrfname);
} else {
- const char *ifname = yang_dnode_get_string(dnode, "./name");
+ const char *ifname = yang_dnode_get_string(dnode, "name");
vty_out(vty, "interface %s", ifname);
}
@@ -1360,6 +1340,15 @@ static void cli_show_interface_end(struct vty *vty,
vty_out(vty, "exit\n");
}
+static int cli_cmp_interface(const struct lyd_node *dnode1,
+ const struct lyd_node *dnode2)
+{
+ const char *ifname1 = yang_dnode_get_string(dnode1, "name");
+ const char *ifname2 = yang_dnode_get_string(dnode2, "name");
+
+ return if_cmp_name_func(ifname1, ifname2);
+}
+
void if_vty_config_start(struct vty *vty, struct interface *ifp)
{
vty_frame(vty, "!\n");
@@ -1476,17 +1465,6 @@ void if_cmd_init_default(void)
if_cmd_init(if_nb_config_write);
}
-void if_zapi_callbacks(int (*create)(struct interface *ifp),
- int (*up)(struct interface *ifp),
- int (*down)(struct interface *ifp),
- int (*destroy)(struct interface *ifp))
-{
- ifp_master.create_hook = create;
- ifp_master.up_hook = up;
- ifp_master.down_hook = down;
- ifp_master.destroy_hook = destroy;
-}
-
/* ------- Northbound callbacks ------- */
/*
@@ -1497,7 +1475,7 @@ static int lib_interface_create(struct nb_cb_create_args *args)
const char *ifname;
struct interface *ifp;
- ifname = yang_dnode_get_string(args->dnode, "./name");
+ ifname = yang_dnode_get_string(args->dnode, "name");
switch (args->event) {
case NB_EV_VALIDATE:
@@ -1708,7 +1686,7 @@ lib_interface_state_mtu_get_elem(struct nb_cb_get_elem_args *args)
{
const struct interface *ifp = args->list_entry;
- return yang_data_new_uint16(args->xpath, ifp->mtu);
+ return yang_data_new_uint32(args->xpath, ifp->mtu);
}
/*
@@ -1779,6 +1757,8 @@ lib_interface_state_phy_address_get_elem(struct nb_cb_get_elem_args *args)
}
/* clang-format off */
+
+/* cli_show callbacks are kept here for daemons not yet converted to mgmtd */
const struct frr_yang_module_info frr_interface_info = {
.name = "frr-interface",
.nodes = {
@@ -1789,6 +1769,7 @@ const struct frr_yang_module_info frr_interface_info = {
.destroy = lib_interface_destroy,
.cli_show = cli_show_interface,
.cli_show_end = cli_show_interface_end,
+ .cli_cmp = cli_cmp_interface,
.get_next = lib_interface_get_next,
.get_keys = lib_interface_get_keys,
.lookup_entry = lib_interface_lookup_entry,
@@ -1861,3 +1842,27 @@ const struct frr_yang_module_info frr_interface_info = {
},
}
};
+
+const struct frr_yang_module_info frr_interface_cli_info = {
+ .name = "frr-interface",
+ .ignore_cfg_cbs = true,
+ .nodes = {
+ {
+ .xpath = "/frr-interface:lib/interface",
+ .cbs = {
+ .cli_show = cli_show_interface,
+ .cli_show_end = cli_show_interface_end,
+ .cli_cmp = cli_cmp_interface,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/description",
+ .cbs = {
+ .cli_show = cli_show_interface_desc,
+ },
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
diff --git a/lib/if.h b/lib/if.h
index c6b4fd216a..0dc56bd210 100644
--- a/lib/if.h
+++ b/lib/if.h
@@ -88,8 +88,6 @@ enum zebra_link_type {
FreeBSD define value in /usr/include/net/if.h.
#define IFNAMSIZ 16
*/
-
-#define INTERFACE_NAMSIZ IFNAMSIZ
#define INTERFACE_HWADDR_MAX 20
typedef signed int ifindex_t;
@@ -197,7 +195,7 @@ struct if_link_params {
uint32_t min_delay; /* Link Min Delay */
uint32_t max_delay; /* Link Max Delay */
uint32_t delay_var; /* Link Delay Variation */
- float pkt_loss; /* Link Packet Loss */
+ uint32_t pkt_loss; /* Link Packet Loss */
float res_bw; /* Residual Bandwidth */
float ava_bw; /* Available Bandwidth */
float use_bw; /* Utilized Bandwidth */
@@ -206,6 +204,8 @@ struct if_link_params {
#define INTERFACE_LINK_PARAMS_SIZE sizeof(struct if_link_params)
#define HAS_LINK_PARAMS(ifp) ((ifp)->link_params != NULL)
+PREDECL_DLIST(if_connected);
+
/* Interface structure */
struct interface {
RB_ENTRY(interface) name_entry, index_entry;
@@ -218,7 +218,7 @@ struct interface {
To delete, just set ifindex to IFINDEX_INTERNAL to indicate that the
interface does not exist in the kernel.
*/
- char name[INTERFACE_NAMSIZ];
+ char name[IFNAMSIZ];
/* Interface index (should be IFINDEX_INTERNAL for non-kernel or
deleted interfaces).
@@ -252,6 +252,9 @@ struct interface {
/* Interface Speed in Mb/s */
uint32_t speed;
+ /* TX queue len */
+ uint32_t txqlen;
+
/* Interface MTU. */
unsigned int mtu; /* IPv4 MTU */
unsigned int
@@ -272,12 +275,8 @@ struct interface {
/* description of the interface. */
char *desc;
- /* Distribute list. */
- void *distribute_in;
- void *distribute_out;
-
/* Connected address list. */
- struct list *connected;
+ struct if_connected_head connected[1];
/* Neighbor connected address list. */
struct list *nbr_connected;
@@ -372,9 +371,6 @@ DECLARE_QOBJ_TYPE(interface);
if (vrf) \
RB_FOREACH (ifp, if_name_head, &vrf->ifaces_by_name)
-#define FOR_ALL_INTERFACES_ADDRESSES(ifp, connected, node) \
- for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected))
-
/* called from the library code whenever interfaces are created/deleted
* note: interfaces may not be fully realized at that point; also they
* may not exist in the system (ifindex = IFINDEX_INTERNAL)
@@ -383,13 +379,34 @@ DECLARE_QOBJ_TYPE(interface);
* can use 1000+ so they run after the daemon has initialised daemon-specific
* interface data
*/
-DECLARE_HOOK(if_add, (struct interface * ifp), (ifp));
-DECLARE_KOOH(if_del, (struct interface * ifp), (ifp));
+DECLARE_HOOK(if_add, (struct interface *ifp), (ifp));
+DECLARE_KOOH(if_del, (struct interface *ifp), (ifp));
+
+/* called (in daemons) when ZAPI tells us the interface actually exists
+ * (ifindex != IFINDEX_INTERNAL)
+ *
+ * WARNING: these 2 hooks NEVER CALLED inside zebra!
+ */
+DECLARE_HOOK(if_real, (struct interface *ifp), (ifp));
+DECLARE_KOOH(if_unreal, (struct interface *ifp), (ifp));
+
+/* called (in daemons) on state changes on interfaces. Whether this is admin
+ * state (= pure config) or carrier state (= hardware link plugged) depends on
+ * zebra's "link-detect" configuration. By default, it's carrier state, so
+ * this won't happen until the interface actually has a link.
+ *
+ * WARNING: these 2 hooks NEVER CALLED inside zebra!
+ */
+DECLARE_HOOK(if_up, (struct interface *ifp), (ifp));
+DECLARE_KOOH(if_down, (struct interface *ifp), (ifp));
+
#define METRIC_MAX (~0)
/* Connected address structure. */
struct connected {
+ struct if_connected_item item;
+
/* Attached interface. */
struct interface *ifp;
@@ -417,6 +434,8 @@ struct connected {
#define ZEBRA_IFA_SECONDARY (1 << 0)
#define ZEBRA_IFA_PEER (1 << 1)
#define ZEBRA_IFA_UNNUMBERED (1 << 2)
+#define ZEBRA_IFA_NOPREFIXROUTE (1 << 3)
+
/* N.B. the ZEBRA_IFA_PEER flag should be set if and only if
a peer address has been configured. If this flag is set,
the destination field must contain the peer address.
@@ -439,6 +458,8 @@ struct connected {
uint32_t metric;
};
+DECLARE_DLIST(if_connected, struct connected, item);
+
/* Nbr Connected address structure. */
struct nbr_connected {
/* Attached interface. */
@@ -509,9 +530,9 @@ extern int if_cmp_name_func(const char *p1, const char *p2);
* This is useful for vrf route-leaking. So more than anything
* else think before you use VRF_UNKNOWN
*/
-extern void if_update_to_new_vrf(struct interface *, vrf_id_t vrf_id);
+extern void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id);
-extern struct interface *if_lookup_by_index(ifindex_t, vrf_id_t vrf_id);
+extern struct interface *if_lookup_by_index(ifindex_t ifindex, vrf_id_t vrf_id);
extern struct interface *if_vrf_lookup_by_index_next(ifindex_t ifindex,
vrf_id_t vrf_id);
extern struct interface *if_lookup_address_local(const void *matchaddr,
@@ -542,7 +563,7 @@ extern int if_set_index(struct interface *ifp, ifindex_t ifindex);
/* Delete the interface, but do not free the structure, and leave it in the
interface list. It is often advisable to leave the pseudo interface
structure because there may be configuration information attached. */
-extern void if_delete_retain(struct interface *);
+extern void if_delete_retain(struct interface *ifp);
/* Delete and free the interface structure: calls if_delete_retain and then
deletes it from the interface list and frees the structure. */
@@ -560,13 +581,13 @@ extern int if_is_pointopoint(const struct interface *ifp);
extern int if_is_multicast(const struct interface *ifp);
extern void if_terminate(struct vrf *vrf);
extern void if_dump_all(void);
-extern const char *if_flag_dump(unsigned long);
-extern const char *if_link_type_str(enum zebra_link_type);
+extern const char *if_flag_dump(unsigned long flags);
+extern const char *if_link_type_str(enum zebra_link_type zlt);
/* Please use ifindex2ifname instead of if_indextoname where possible;
ifindex2ifname uses internal interface info, whereas if_indextoname must
make a system call. */
-extern const char *ifindex2ifname(ifindex_t, vrf_id_t vrf_id);
+extern const char *ifindex2ifname(ifindex_t ifindex, vrf_id_t vrf_id);
/* Please use ifname2ifindex instead of if_nametoindex where possible;
ifname2ifindex uses internal interface info, whereas if_nametoindex must
@@ -576,19 +597,20 @@ extern ifindex_t ifname2ifindex(const char *ifname, vrf_id_t vrf_id);
/* Connected address functions. */
extern struct connected *connected_new(void);
extern void connected_free(struct connected **connected);
-extern void connected_add(struct interface *, struct connected *);
-extern struct connected *
-connected_add_by_prefix(struct interface *, struct prefix *, struct prefix *);
-extern struct connected *connected_delete_by_prefix(struct interface *,
- struct prefix *);
-extern struct connected *connected_lookup_prefix(struct interface *,
- const struct prefix *);
-extern struct connected *connected_lookup_prefix_exact(struct interface *,
- const struct prefix *);
-extern unsigned int connected_count_by_family(struct interface *, int family);
+extern struct connected *connected_add_by_prefix(struct interface *ifp,
+ struct prefix *p,
+ struct prefix *dest);
+extern struct connected *connected_delete_by_prefix(struct interface *ifp,
+ struct prefix *p);
+extern struct connected *connected_lookup_prefix(struct interface *ifp,
+ const struct prefix *p);
+extern struct connected *connected_lookup_prefix_exact(struct interface *ifp,
+ const struct prefix *p);
+extern unsigned int connected_count_by_family(struct interface *ifp, int family);
extern struct nbr_connected *nbr_connected_new(void);
-extern void nbr_connected_free(struct nbr_connected *);
-struct nbr_connected *nbr_connected_check(struct interface *, struct prefix *);
+extern void nbr_connected_free(struct nbr_connected *connected);
+struct nbr_connected *nbr_connected_check(struct interface *ifp,
+ struct prefix *p);
struct connected *connected_get_linklocal(struct interface *ifp);
/* link parameters */
@@ -596,10 +618,10 @@ bool if_link_params_cmp(struct if_link_params *iflp1,
struct if_link_params *iflp2);
void if_link_params_copy(struct if_link_params *dst,
struct if_link_params *src);
-struct if_link_params *if_link_params_get(struct interface *);
+struct if_link_params *if_link_params_get(struct interface *ifp);
struct if_link_params *if_link_params_enable(struct interface *ifp);
struct if_link_params *if_link_params_init(struct interface *ifp);
-void if_link_params_free(struct interface *);
+void if_link_params_free(struct interface *ifp);
/* Northbound. */
struct vty;
@@ -607,10 +629,6 @@ extern void if_vty_config_start(struct vty *vty, struct interface *ifp);
extern void if_vty_config_end(struct vty *vty);
extern void if_cmd_init(int (*config_write)(struct vty *));
extern void if_cmd_init_default(void);
-extern void if_zapi_callbacks(int (*create)(struct interface *ifp),
- int (*up)(struct interface *ifp),
- int (*down)(struct interface *ifp),
- int (*destroy)(struct interface *ifp));
extern void if_new_via_zapi(struct interface *ifp);
extern void if_up_via_zapi(struct interface *ifp);
@@ -618,6 +636,7 @@ extern void if_down_via_zapi(struct interface *ifp);
extern void if_destroy_via_zapi(struct interface *ifp);
extern const struct frr_yang_module_info frr_interface_info;
+extern const struct frr_yang_module_info frr_interface_cli_info;
#ifdef __cplusplus
}
diff --git a/lib/if_rmap.c b/lib/if_rmap.c
index 42e162072f..5fe5061a3c 100644
--- a/lib/if_rmap.c
+++ b/lib/if_rmap.c
@@ -243,14 +243,14 @@ DEFPY_YANG(no_if_ipv6_route_map, no_if_ipv6_route_map_cmd,
void cli_show_if_route_map(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults)
{
- if (yang_dnode_exists(dnode, "./in-route-map"))
+ if (yang_dnode_exists(dnode, "in-route-map"))
vty_out(vty, " route-map %s in %s\n",
- yang_dnode_get_string(dnode, "./in-route-map"),
- yang_dnode_get_string(dnode, "./interface"));
- if (yang_dnode_exists(dnode, "./out-route-map"))
+ yang_dnode_get_string(dnode, "in-route-map"),
+ yang_dnode_get_string(dnode, "interface"));
+ if (yang_dnode_exists(dnode, "out-route-map"))
vty_out(vty, " route-map %s out %s\n",
- yang_dnode_get_string(dnode, "./out-route-map"),
- yang_dnode_get_string(dnode, "./interface"));
+ yang_dnode_get_string(dnode, "out-route-map"),
+ yang_dnode_get_string(dnode, "interface"));
}
void if_rmap_yang_modify_cb(struct if_rmap_ctx *ctx,
diff --git a/lib/imsg-buffer.c b/lib/imsg-buffer.c
index 4f041ff66e..556e0cf904 100644
--- a/lib/imsg-buffer.c
+++ b/lib/imsg-buffer.c
@@ -6,6 +6,7 @@
*/
#include <zebra.h>
+#include <sys/uio.h>
#include "queue.h"
#include "imsg.h"
diff --git a/lib/ipaddr.h b/lib/ipaddr.h
index e3ad14d7db..c86e38c867 100644
--- a/lib/ipaddr.h
+++ b/lib/ipaddr.h
@@ -28,6 +28,7 @@ struct ipaddr {
enum ipaddr_type_t ipa_type;
union {
uint8_t addr;
+ uint8_t addrbytes[16];
struct in_addr _v4_addr;
struct in6_addr _v6_addr;
} ip;
diff --git a/lib/jhash.c b/lib/jhash.c
index 0d561ef3a4..4e02112e09 100644
--- a/lib/jhash.c
+++ b/lib/jhash.c
@@ -86,34 +86,34 @@ uint32_t jhash(const void *key, uint32_t length, uint32_t initval)
switch (len) {
case 11:
c += ((uint32_t)k[10] << 24);
- /* fallthru */
+ fallthrough;
case 10:
c += ((uint32_t)k[9] << 16);
- /* fallthru */
+ fallthrough;
case 9:
c += ((uint32_t)k[8] << 8);
- /* fallthru */
+ fallthrough;
case 8:
b += ((uint32_t)k[7] << 24);
- /* fallthru */
+ fallthrough;
case 7:
b += ((uint32_t)k[6] << 16);
- /* fallthru */
+ fallthrough;
case 6:
b += ((uint32_t)k[5] << 8);
- /* fallthru */
+ fallthrough;
case 5:
b += k[4];
- /* fallthru */
+ fallthrough;
case 4:
a += ((uint32_t)k[3] << 24);
- /* fallthru */
+ fallthrough;
case 3:
a += ((uint32_t)k[2] << 16);
- /* fallthru */
+ fallthrough;
case 2:
a += ((uint32_t)k[1] << 8);
- /* fallthru */
+ fallthrough;
case 1:
a += k[0];
}
@@ -148,7 +148,7 @@ uint32_t jhash2(const uint32_t *k, uint32_t length, uint32_t initval)
switch (len) {
case 2:
b += k[1];
- /* fallthru */
+ fallthrough;
case 1:
a += k[0];
}
diff --git a/lib/keychain.c b/lib/keychain.c
index 640746bb41..1982220bc7 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)
{
@@ -37,6 +37,7 @@ static void keychain_free(struct keychain *keychain)
static struct key *key_new(void)
{
struct key *key = XCALLOC(MTYPE_KEY, sizeof(struct key));
+
QOBJ_REG(key, key);
return key;
}
@@ -77,11 +78,11 @@ static int key_cmp_func(void *arg1, void *arg2)
static void key_delete_func(struct key *key)
{
if (key->string)
- free(key->string);
+ XFREE(MTYPE_KEY, key->string);
key_free(key);
}
-static struct keychain *keychain_get(const char *name)
+struct keychain *keychain_get(const char *name)
{
struct keychain *keychain;
@@ -100,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);
@@ -109,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;
@@ -182,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;
@@ -199,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);
@@ -207,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,
@@ -393,864 +278,39 @@ 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)
+void keychain_terminate(void)
{
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);
+ while (listcount(keychain_list)) {
+ keychain = listgetdata(listhead(keychain_list));
- 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");
+ listnode_delete(keychain_list, keychain);
+ keychain_delete(keychain);
}
- return 0;
+ list_delete(&keychain_list);
}
-
-static void keychain_active_config(vector comps, struct cmd_token *token)
+void keychain_init_new(bool in_backend)
{
- struct keychain *keychain;
- struct listnode *node;
+ keychain_list = list_new();
- for (ALL_LIST_ELEMENTS_RO(keychain_list, node, keychain))
- vector_set(comps, XSTRDUP(MTYPE_COMPLETION, keychain->name));
+ if (!in_backend)
+ keychain_cli_init();
}
-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_init(void)
{
- 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);
-
- 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);
+ 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 be93275a5d..dc35c2ea39 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,12 +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 0000000000..26f56f182a
--- /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 0000000000..6838268a93
--- /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, "../../name");
+ keychain = keychain_lookup(name);
+ index = (uint32_t)yang_dnode_get_uint64(args->dnode, "../key-id");
+ key = key_lookup(keychain, index);
+ 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);
+ 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/ldp_sync.h b/lib/ldp_sync.h
index f7601ebf9d..3a6ae5b357 100644
--- a/lib/ldp_sync.h
+++ b/lib/ldp_sync.h
@@ -59,7 +59,7 @@ struct ldp_igp_sync_if_state {
struct ldp_igp_sync_if_state_req {
int proto;
ifindex_t ifindex;
- char name[INTERFACE_NAMSIZ];
+ char name[IFNAMSIZ];
};
#ifdef __cplusplus
diff --git a/lib/lib_errors.c b/lib/lib_errors.c
index a96fac9cd4..9d6c04325c 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 8cdfb166c7..9e0d539599 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/libfrr.c b/lib/libfrr.c
index 07e2eafec5..03025328b7 100644
--- a/lib/libfrr.c
+++ b/lib/libfrr.c
@@ -6,7 +6,11 @@
*/
#include <zebra.h>
+
+#include <signal.h>
+#include <sys/stat.h>
#include <sys/un.h>
+#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -33,6 +37,8 @@
#include "frrscript.h"
#include "systemd.h"
+#include "lib/config_paths.h"
+
DEFINE_HOOK(frr_early_init, (struct event_loop * tm), (tm));
DEFINE_HOOK(frr_late_init, (struct event_loop * tm), (tm));
DEFINE_HOOK(frr_config_pre, (struct event_loop * tm), (tm));
@@ -41,10 +47,8 @@ DEFINE_KOOH(frr_early_fini, (), ());
DEFINE_KOOH(frr_fini, (), ());
const char frr_sysconfdir[] = SYSCONFDIR;
-char frr_vtydir[256];
-#ifdef HAVE_SQLITE3
-const char frr_dbdir[] = DAEMON_DB_DIR;
-#endif
+char frr_runstatedir[256] = FRR_RUNSTATE_PATH;
+char frr_libstatedir[256] = FRR_LIBSTATE_PATH;
const char frr_moduledir[] = MODULE_PATH;
const char frr_scriptdir[] = SCRIPT_PATH;
@@ -52,7 +56,7 @@ char frr_protoname[256] = "NONE";
char frr_protonameinst[256] = "NONE";
char config_default[512];
-char frr_zclientpath[256];
+char frr_zclientpath[512];
static char pidfile_default[1024];
#ifdef HAVE_SQLITE3
static char dbfile_default[512];
@@ -130,6 +134,7 @@ static const struct optspec os_always = {
" --limit-fds Limit number of fds supported\n",
lo_always};
+static bool logging_to_stdout = false; /* set when --log stdout specified */
static const struct option lo_cfg[] = {
{"config_file", required_argument, NULL, 'f'},
@@ -203,7 +208,7 @@ bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len,
if (!strncmp(path, ZAPI_TCP_PATHNAME, strlen(ZAPI_TCP_PATHNAME))) {
/* note: this functionality is disabled at bottom */
int af;
- int port = ZEBRA_PORT;
+ int port = ZEBRA_TCP_PORT;
char *err = NULL;
struct sockaddr_in *sin = NULL;
struct sockaddr_in6 *sin6 = NULL;
@@ -217,7 +222,8 @@ bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len,
break;
case '6':
path++;
- /* fallthrough */
+ af = AF_INET6;
+ break;
default:
af = AF_INET6;
break;
@@ -304,11 +310,6 @@ bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len,
static struct frr_daemon_info *di = NULL;
-void frr_init_vtydir(void)
-{
- snprintf(frr_vtydir, sizeof(frr_vtydir), DAEMON_VTY_DIR, "", "");
-}
-
void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv)
{
di = daemon;
@@ -318,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);
@@ -338,16 +344,14 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv)
if (di->flags & FRR_DETACH_LATER)
nodetach_daemon = true;
- frr_init_vtydir();
snprintf(config_default, sizeof(config_default), "%s/%s.conf",
frr_sysconfdir, di->name);
snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid",
- frr_vtydir, di->name);
- snprintf(frr_zclientpath, sizeof(frr_zclientpath),
- ZEBRA_SERV_PATH, "", "");
+ frr_runstatedir, di->name);
+ snprintf(frr_zclientpath, sizeof(frr_zclientpath), ZAPI_SOCK_NAME);
#ifdef HAVE_SQLITE3
snprintf(dbfile_default, sizeof(dbfile_default), "%s/%s.db",
- frr_dbdir, di->name);
+ frr_libstatedir, di->name);
#endif
strlcpy(frr_protoname, di->logname, sizeof(frr_protoname));
@@ -496,13 +500,15 @@ static int frr_opt(int opt)
}
di->pathspace = optarg;
+ snprintf(frr_runstatedir, sizeof(frr_runstatedir),
+ FRR_RUNSTATE_PATH "/%s", di->pathspace);
+ snprintf(frr_libstatedir, sizeof(frr_libstatedir),
+ FRR_LIBSTATE_PATH "/%s", di->pathspace);
+ snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid",
+ frr_runstatedir, di->name);
if (!di->zpathspace)
snprintf(frr_zclientpath, sizeof(frr_zclientpath),
- ZEBRA_SERV_PATH, "/", di->pathspace);
- snprintf(frr_vtydir, sizeof(frr_vtydir), DAEMON_VTY_DIR, "/",
- di->pathspace);
- snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid",
- frr_vtydir, di->name);
+ ZAPI_SOCK_NAME);
break;
case 'o':
vrf_set_default_name(optarg);
@@ -723,10 +729,10 @@ struct event_loop *frr_init(void)
snprintf(config_default, sizeof(config_default), "%s%s%s%s.conf",
frr_sysconfdir, p_pathspace, di->name, p_instance);
snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s%s.pid",
- frr_vtydir, di->name, p_instance);
+ frr_runstatedir, di->name, p_instance);
#ifdef HAVE_SQLITE3
snprintf(dbfile_default, sizeof(dbfile_default), "%s/%s%s%s.db",
- frr_dbdir, p_pathspace, di->name, p_instance);
+ frr_libstatedir, p_pathspace, di->name, p_instance);
#endif
zprivs_preinit(di->privs);
@@ -738,6 +744,11 @@ struct event_loop *frr_init(void)
while ((log_arg = log_args_pop(di->early_logging))) {
command_setup_early_logging(log_arg->target,
di->early_loglevel);
+ /* this is a bit of a hack,
+ but need to notice when
+ the target is stdout */
+ if (strcmp(log_arg->target, "stdout") == 0)
+ logging_to_stdout = true;
XFREE(MTYPE_TMP, log_arg);
}
@@ -750,8 +761,9 @@ struct event_loop *frr_init(void)
/* don't mkdir these as root... */
if (!(di->flags & FRR_NO_PRIVSEP)) {
+ frr_mkdir(frr_libstatedir, false);
if (!di->pid_file || !di->vty_path)
- frr_mkdir(frr_vtydir, false);
+ frr_mkdir(frr_runstatedir, false);
if (di->pid_file)
frr_mkdir(di->pid_file, true);
if (di->vty_path)
@@ -950,8 +962,6 @@ static void frr_daemonize(void)
}
close(fds[1]);
- nb_terminate();
- yang_terminate();
frr_daemon_wait(fds[0]);
}
@@ -1030,7 +1040,7 @@ void frr_config_fork(void)
zlog_tls_buffer_init();
}
-static void frr_vty_serv(void)
+void frr_vty_serv_start(void)
{
/* allow explicit override of vty_path in the future
* (not currently set anywhere) */
@@ -1038,7 +1048,7 @@ static void frr_vty_serv(void)
const char *dir;
char defvtydir[256];
- snprintf(defvtydir, sizeof(defvtydir), "%s", frr_vtydir);
+ snprintf(defvtydir, sizeof(defvtydir), "%s", frr_runstatedir);
dir = di->vty_sock_path ? di->vty_sock_path : defvtydir;
@@ -1052,7 +1062,15 @@ static void frr_vty_serv(void)
di->vty_path = vtypath_default;
}
- vty_serv_sock(di->vty_addr, di->vty_port, di->vty_path);
+ vty_serv_start(di->vty_addr, di->vty_port, di->vty_path);
+}
+
+void frr_vty_serv_stop(void)
+{
+ vty_serv_stop();
+
+ if (di->vty_path)
+ unlink(di->vty_path);
}
static void frr_check_detach(void)
@@ -1088,9 +1106,15 @@ static void frr_terminal_close(int isexit)
"%s: failed to open /dev/null: %s", __func__,
safe_strerror(errno));
} else {
- dup2(nullfd, 0);
- dup2(nullfd, 1);
- dup2(nullfd, 2);
+ int fd;
+ /*
+ * only redirect stdin, stdout, stderr to null when a tty also
+ * don't redirect when stdout is set with --log stdout
+ */
+ for (fd = 2; fd >= 0; fd--)
+ if (isatty(fd) &&
+ (fd != STDOUT_FILENO || !logging_to_stdout))
+ dup2(nullfd, fd);
close(nullfd);
}
}
@@ -1143,7 +1167,8 @@ void frr_run(struct event_loop *master)
{
char instanceinfo[64] = "";
- frr_vty_serv();
+ if (!(di->flags & FRR_MANUAL_VTY_START))
+ frr_vty_serv_start();
if (di->instance)
snprintf(instanceinfo, sizeof(instanceinfo), "instance %u ",
@@ -1168,9 +1193,16 @@ void frr_run(struct event_loop *master)
"%s: failed to open /dev/null: %s",
__func__, safe_strerror(errno));
} else {
- dup2(nullfd, 0);
- dup2(nullfd, 1);
- dup2(nullfd, 2);
+ int fd;
+ /*
+ * only redirect stdin, stdout, stderr to null when a
+ * tty also don't redirect when stdout is set with --log
+ * stdout
+ */
+ for (fd = 2; fd >= 0; fd--)
+ if (isatty(fd) &&
+ (fd != STDOUT_FILENO || !logging_to_stdout))
+ dup2(nullfd, fd);
close(nullfd);
}
@@ -1194,7 +1226,7 @@ void frr_fini(void)
{
FILE *fp;
char filename[128];
- int have_leftovers;
+ int have_leftovers = 0;
hook_call(frr_fini);
@@ -1220,16 +1252,15 @@ void frr_fini(void)
/* frrmod_init -> nothing needed / hooks */
rcu_shutdown();
- if (!debug_memstats_at_exit)
- return;
-
- have_leftovers = log_memstats(stderr, di->name);
+ /* also log memstats to stderr when stderr goes to a file*/
+ if (debug_memstats_at_exit || !isatty(STDERR_FILENO))
+ have_leftovers = log_memstats(stderr, di->name);
/* in case we decide at runtime that we want exit-memstats for
- * a daemon, but it has no stderr because it's daemonized
+ * a daemon
* (only do this if we actually have something to print though)
*/
- if (!have_leftovers)
+ if (!debug_memstats_at_exit || !have_leftovers)
return;
snprintf(filename, sizeof(filename), "/tmp/frr-memstats-%s-%llu-%llu",
@@ -1243,6 +1274,161 @@ void frr_fini(void)
}
}
+struct json_object *frr_daemon_state_load(void)
+{
+ struct json_object *state;
+ char **state_path;
+
+ assertf(di->state_paths,
+ "CODE BUG: daemon trying to load state, but no state path in frr_daemon_info");
+
+ for (state_path = di->state_paths; *state_path; state_path++) {
+ state = json_object_from_file(*state_path);
+ if (state)
+ return state;
+ }
+
+ return json_object_new_object();
+}
+
+/* cross-reference file_write_config() in command.c
+ * the code there is similar but not identical (configs use a unique temporary
+ * name for writing and keep a backup of the previous config.)
+ */
+void frr_daemon_state_save(struct json_object **statep)
+{
+ struct json_object *state = *statep;
+ char *state_path, *slash, *temp_name, **other;
+ size_t name_len, json_len;
+ const char *json_str;
+ int dirfd, fd;
+
+ assertf(di->state_paths,
+ "CODE BUG: daemon trying to save state, but no state path in frr_daemon_info");
+
+ state_path = di->state_paths[0];
+ json_str = json_object_to_json_string_ext(state,
+ JSON_C_TO_STRING_PRETTY);
+ json_len = strlen(json_str);
+
+ /* To correctly fsync() and ensure we have either consistent old state
+ * or consistent new state but no fs-damage garbage inbetween, we need
+ * to work with a directory fd. If we need that anyway we might as
+ * well use the dirfd with openat() & co in fd-relative operations.
+ */
+
+ slash = strrchr(state_path, '/');
+ if (slash) {
+ char *state_dir;
+
+ state_dir = XSTRDUP(MTYPE_TMP, state_path);
+ state_dir[slash - state_path] = '\0';
+ dirfd = open(state_dir, O_DIRECTORY | O_RDONLY);
+ XFREE(MTYPE_TMP, state_dir);
+
+ if (dirfd < 0) {
+ zlog_err("failed to open directory %pSQq for saving daemon state: %m",
+ state_dir);
+ return;
+ }
+
+ /* skip to file name */
+ slash++;
+ } else {
+ dirfd = open(".", O_DIRECTORY | O_RDONLY);
+ if (dirfd < 0) {
+ zlog_err(
+ "failed to open current directory for saving daemon state: %m");
+ return;
+ }
+
+ /* file name = path */
+ slash = state_path;
+ }
+
+ /* unlike saving configs, a temporary unique filename is unhelpful
+ * here as it might litter files on limited write-heavy storage
+ * (think switch with small NOR flash for frequently written data.)
+ *
+ * => always use filename with .sav suffix, worst case it litters one
+ * file.
+ */
+ name_len = strlen(slash);
+ temp_name = XMALLOC(MTYPE_TMP, name_len + 5);
+ memcpy(temp_name, slash, name_len);
+ memcpy(temp_name + name_len, ".sav", 5);
+
+ /* state file is always 0600, it's by and for FRR itself only */
+ fd = openat(dirfd, temp_name, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ if (fd < 0) {
+ zlog_err("failed to open temporary daemon state save file for %pSQq: %m",
+ state_path);
+ goto out_closedir_free;
+ }
+
+ while (json_len) {
+ ssize_t nwr = write(fd, json_str, json_len);
+
+ if (nwr <= 0) {
+ zlog_err("failed to write temporary daemon state to %pSQq: %m",
+ state_path);
+
+ close(fd);
+ unlinkat(dirfd, temp_name, 0);
+ goto out_closedir_free;
+ }
+
+ json_str += nwr;
+ json_len -= nwr;
+ }
+
+ /* fsync is theoretically implicit in close(), but... */
+ if (fsync(fd) < 0)
+ zlog_warn("fsync for daemon state %pSQq failed: %m", state_path);
+ close(fd);
+
+ /* this is the *actual* fsync that ensures we're consistent. The
+ * file fsync only syncs the inode, but not the directory entry
+ * referring to it.
+ */
+ if (fsync(dirfd) < 0)
+ zlog_warn("directory fsync for daemon state %pSQq failed: %m",
+ state_path);
+
+ /* atomic, hopefully. */
+ if (renameat(dirfd, temp_name, dirfd, slash) < 0) {
+ zlog_err("renaming daemon state %pSQq to %pSQq failed: %m",
+ temp_name, state_path);
+ /* no unlink here, give the user a chance to investigate */
+ goto out_closedir_free;
+ }
+
+ /* and the rename needs to be synced too */
+ if (fsync(dirfd) < 0)
+ zlog_warn("directory fsync for daemon state %pSQq failed after rename: %m",
+ state_path);
+
+ /* daemon may specify other deprecated paths to load from; since we
+ * just saved successfully we should delete those.
+ */
+ for (other = di->state_paths + 1; *other; other++) {
+ if (unlink(*other) == 0)
+ continue;
+ if (errno == ENOENT || errno == ENOTDIR)
+ continue;
+
+ zlog_warn("failed to remove deprecated daemon state file %pSQq: %m",
+ *other);
+ }
+
+out_closedir_free:
+ XFREE(MTYPE_TMP, temp_name);
+ close(dirfd);
+
+ json_object_free(state);
+ *statep = NULL;
+}
+
#ifdef INTERP
static const char interp[]
__attribute__((section(".interp"), used)) = INTERP;
@@ -1264,3 +1450,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 c05bc01e4f..77d70448a9 100644
--- a/lib/libfrr.h
+++ b/lib/libfrr.h
@@ -22,6 +22,8 @@
extern "C" {
#endif
+#define ZAPI_SOCK_NAME "%s/zserv.api", frr_runstatedir
+
/* The following options disable specific command line options that
* are not applicable for a particular daemon.
*/
@@ -39,6 +41,11 @@ extern "C" {
* Does nothing if -d isn't used.
*/
#define FRR_DETACH_LATER (1 << 6)
+/* If FRR_MANUAL_VTY_START is used, frr_run() will not automatically start
+ * listening on for vty connection (either TCP or Unix socket based). The daemon
+ * is responsible for calling frr_vty_serv() itself.
+ */
+#define FRR_MANUAL_VTY_START (1 << 7)
PREDECL_DLIST(log_args);
struct log_arg {
@@ -48,6 +55,49 @@ struct log_arg {
};
DECLARE_DLIST(log_args, struct log_arg, itm);
+/* Registry of daemons' port defaults. Many of these are VTY ports; some
+ * daemons have default ports for other features such as ospfapi, or zebra's
+ * FPM.
+ */
+
+/* default zebra TCP port for zapi; this is currently disabled for security
+ * reasons.
+ */
+#define ZEBRA_TCP_PORT 2600
+
+#define ZEBRA_VTY_PORT 2601
+#define RIP_VTY_PORT 2602
+#define RIPNG_VTY_PORT 2603
+#define OSPF_VTY_PORT 2604
+#define BGP_VTY_PORT 2605
+#define OSPF6_VTY_PORT 2606
+
+/* Default API server port to accept connection request from client-side.
+ * This value could be overridden by "ospfapi" entry in "/etc/services".
+ */
+#define OSPF_API_SYNC_PORT 2607
+
+#define ISISD_VTY_PORT 2608
+#define BABEL_VTY_PORT 2609
+#define NHRP_VTY_PORT 2610
+#define PIMD_VTY_PORT 2611
+#define LDP_VTY_PORT 2612
+#define EIGRP_VTY_PORT 2613
+#define SHARP_VTY_PORT 2614
+#define PBR_VTY_PORT 2615
+#define STATIC_VTY_PORT 2616
+#define BFDD_VTY_PORT 2617
+#define FABRICD_VTY_PORT 2618
+#define VRRP_VTY_PORT 2619
+
+/* default port for FPM connections */
+#define FPM_DEFAULT_PORT 2620
+
+#define PATH_VTY_PORT 2621
+#define PIM6D_VTY_PORT 2622
+#define MGMTD_VTY_PORT 2623
+/* Registry of daemons' port defaults */
+
enum frr_cli_mode {
FRR_CLI_CLASSIC = 0,
FRR_CLI_TRANSACTIONAL,
@@ -80,6 +130,7 @@ struct frr_daemon_info {
const char *vty_path;
const char *module_path;
const char *script_path;
+ char **state_paths;
const char *pathspace;
bool zpathspace;
@@ -125,7 +176,6 @@ struct frr_daemon_info {
.version = FRR_VERSION, ); \
MACRO_REQUIRE_SEMICOLON() /* end */
-extern void frr_init_vtydir(void);
extern void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv);
extern void frr_opt_add(const char *optstr, const struct option *longopts,
const char *helpstr);
@@ -150,10 +200,16 @@ 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_stop(void);
extern bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len,
const char *path);
+struct json_object;
+extern struct json_object *frr_daemon_state_load(void);
+extern void frr_daemon_state_save(struct json_object **statep);
+
/* these two are before the protocol daemon does its own shutdown
* it's named this way being the counterpart to frr_late_init */
DECLARE_KOOH(frr_early_fini, (), ());
@@ -163,9 +219,10 @@ DECLARE_KOOH(frr_fini, (), ());
extern void frr_fini(void);
extern char config_default[512];
-extern char frr_zclientpath[256];
+extern char frr_zclientpath[512];
extern const char frr_sysconfdir[];
-extern char frr_vtydir[256];
+extern char frr_runstatedir[256];
+extern char frr_libstatedir[256];
extern const char frr_moduledir[];
extern const char frr_scriptdir[];
@@ -176,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 59320322ca..14f4a3cb20 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 3262534de5..0ac490a00e 100644
--- a/lib/libospf.h
+++ b/lib/libospf.h
@@ -55,19 +55,23 @@ extern "C" {
#define OSPF_ROUTER_DEAD_INTERVAL_DEFAULT 40
#define OSPF_ROUTER_DEAD_INTERVAL_MINIMAL 1
#define OSPF_HELLO_INTERVAL_DEFAULT 10
+#define OSPF_HELLO_DELAY_DEFAULT 10
#define OSPF_ROUTER_PRIORITY_DEFAULT 1
#define OSPF_RETRANSMIT_INTERVAL_DEFAULT 5
#define OSPF_TRANSMIT_DELAY_DEFAULT 1
#define OSPF_DEFAULT_BANDWIDTH 10000 /* Mbps */
-#define OSPF_DEFAULT_REF_BANDWIDTH 100000 /* Mbps */
+#define OSPF_DEFAULT_REF_BANDWIDTH 100000 /* Kbps */
#define OSPF_POLL_INTERVAL_DEFAULT 60
#define OSPF_NEIGHBOR_PRIORITY_DEFAULT 0
#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 */
#define OSPF_AREA_RANGE_COST_UNSPEC -1U
diff --git a/lib/link_state.c b/lib/link_state.c
index 752030cd47..25373bdb20 100644
--- a/lib/link_state.c
+++ b/lib/link_state.c
@@ -497,7 +497,6 @@ void ls_vertex_del(struct ls_ted *ted, struct ls_vertex *vertex)
/* Then remove Vertex from Link State Data Base and free memory */
vertices_del(&ted->vertices, vertex);
XFREE(MTYPE_LS_DB, vertex);
- vertex = NULL;
}
void ls_vertex_del_all(struct ls_ted *ted, struct ls_vertex *vertex)
@@ -524,7 +523,9 @@ struct ls_vertex *ls_vertex_update(struct ls_ted *ted, struct ls_node *node)
if (!ls_node_same(old->node, node)) {
ls_node_del(old->node);
old->node = node;
- }
+ } else
+ ls_node_del(node);
+
old->status = UPDATE;
return old;
}
@@ -806,7 +807,9 @@ struct ls_edge *ls_edge_update(struct ls_ted *ted,
if (!ls_attributes_same(old->attributes, attributes)) {
ls_attributes_del(old->attributes);
old->attributes = attributes;
- }
+ } else
+ ls_attributes_del(attributes);
+
old->status = UPDATE;
return old;
}
@@ -903,7 +906,9 @@ struct ls_subnet *ls_subnet_update(struct ls_ted *ted, struct ls_prefix *pref)
if (!ls_prefix_same(old->ls_pref, pref)) {
ls_prefix_del(old->ls_pref);
old->ls_pref = pref;
- }
+ } else
+ ls_prefix_del(pref);
+
old->status = UPDATE;
return old;
}
@@ -956,7 +961,10 @@ struct ls_subnet *ls_find_subnet(struct ls_ted *ted,
{
struct ls_subnet subnet = {};
- subnet.key = *prefix;
+ if (!prefix)
+ return NULL;
+
+ prefix_copy(&subnet.key, prefix);
return subnets_find(&ted->subnets, &subnet);
}
@@ -1136,31 +1144,13 @@ int ls_unregister(struct zclient *zclient, bool server)
int ls_request_sync(struct zclient *zclient)
{
- struct stream *s;
- uint16_t flags = 0;
-
/* Check buffer size */
if (STREAM_SIZE(zclient->obuf)
< (ZEBRA_HEADER_SIZE + 3 * sizeof(uint32_t)))
return -1;
- s = zclient->obuf;
- stream_reset(s);
-
- zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT);
-
- /* Set type and flags */
- stream_putl(s, LINK_STATE_SYNC);
- stream_putw(s, flags);
- /* Send destination client info */
- stream_putc(s, zclient->redist_default);
- stream_putw(s, zclient->instance);
- stream_putl(s, zclient->session_id);
-
- /* Put length into the header at the start of the stream. */
- stream_putw_at(s, 0, stream_get_endp(s));
-
- return zclient_send_message(zclient);
+ /* No data with this message */
+ return zclient_send_opaque(zclient, LINK_STATE_SYNC, NULL, 0);
}
static struct ls_node *ls_parse_node(struct stream *s)
@@ -1621,23 +1611,15 @@ int ls_send_msg(struct zclient *zclient, struct ls_message *msg,
(ZEBRA_HEADER_SIZE + sizeof(uint32_t) + sizeof(msg)))
return -1;
+ /* Init the message, then encode the data inline. */
+ if (dst == NULL)
+ zapi_opaque_init(zclient, LINK_STATE_UPDATE, flags);
+ else
+ zapi_opaque_unicast_init(zclient, LINK_STATE_UPDATE, flags,
+ dst->proto, dst->instance,
+ dst->session_id);
+
s = zclient->obuf;
- stream_reset(s);
-
- zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT);
-
- /* Set sub-type, flags and destination for unicast message */
- stream_putl(s, LINK_STATE_UPDATE);
- if (dst != NULL) {
- SET_FLAG(flags, ZAPI_OPAQUE_FLAG_UNICAST);
- stream_putw(s, flags);
- /* Send destination client info */
- stream_putc(s, dst->proto);
- stream_putw(s, dst->instance);
- stream_putl(s, dst->session_id);
- } else {
- stream_putw(s, flags);
- }
/* Format Link State message */
if (ls_format_msg(s, msg) < 0) {
@@ -1757,7 +1739,7 @@ struct ls_message *ls_subnet2msg(struct ls_message *msg,
struct ls_vertex *ls_msg2vertex(struct ls_ted *ted, struct ls_message *msg,
bool delete)
{
- struct ls_node *node = (struct ls_node *)msg->data.node;
+ struct ls_node *node = msg->data.node;
struct ls_vertex *vertex = NULL;
switch (msg->event) {
@@ -1779,9 +1761,10 @@ struct ls_vertex *ls_msg2vertex(struct ls_ted *ted, struct ls_message *msg,
case LS_MSG_EVENT_DELETE:
vertex = ls_find_vertex_by_id(ted, node->adv);
if (vertex) {
- if (delete)
+ if (delete) {
ls_vertex_del_all(ted, vertex);
- else
+ vertex = NULL;
+ } else
vertex->status = DELETE;
}
break;
@@ -1796,7 +1779,7 @@ struct ls_vertex *ls_msg2vertex(struct ls_ted *ted, struct ls_message *msg,
struct ls_edge *ls_msg2edge(struct ls_ted *ted, struct ls_message *msg,
bool delete)
{
- struct ls_attributes *attr = (struct ls_attributes *)msg->data.attr;
+ struct ls_attributes *attr = msg->data.attr;
struct ls_edge *edge = NULL;
switch (msg->event) {
@@ -1836,7 +1819,7 @@ struct ls_edge *ls_msg2edge(struct ls_ted *ted, struct ls_message *msg,
struct ls_subnet *ls_msg2subnet(struct ls_ted *ted, struct ls_message *msg,
bool delete)
{
- struct ls_prefix *pref = (struct ls_prefix *)msg->data.prefix;
+ struct ls_prefix *pref = msg->data.prefix;
struct ls_subnet *subnet = NULL;
switch (msg->event) {
@@ -1858,9 +1841,10 @@ struct ls_subnet *ls_msg2subnet(struct ls_ted *ted, struct ls_message *msg,
case LS_MSG_EVENT_DELETE:
subnet = ls_find_subnet(ted, &pref->pref);
if (subnet) {
- if (delete)
+ if (delete) {
ls_subnet_del_all(ted, subnet);
- else
+ subnet = NULL;
+ } else
subnet->status = DELETE;
}
break;
diff --git a/lib/log.c b/lib/log.c
index 00b897dca1..969ca79256 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -8,6 +8,10 @@
#include <zebra.h>
+#ifdef HAVE_GLIBC_BACKTRACE
+#include <execinfo.h>
+#endif /* HAVE_GLIBC_BACKTRACE */
+
#include "zclient.h"
#include "log.h"
#include "memory.h"
@@ -351,7 +355,6 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY(ZEBRA_VRF_ADD),
DESC_ENTRY(ZEBRA_VRF_DELETE),
DESC_ENTRY(ZEBRA_VRF_LABEL),
- DESC_ENTRY(ZEBRA_INTERFACE_VRF_UPDATE),
DESC_ENTRY(ZEBRA_BFD_CLIENT_REGISTER),
DESC_ENTRY(ZEBRA_BFD_CLIENT_DEREGISTER),
DESC_ENTRY(ZEBRA_INTERFACE_ENABLE_RADV),
@@ -441,11 +444,11 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY(ZEBRA_NEIGH_DISCOVER),
DESC_ENTRY(ZEBRA_ROUTE_NOTIFY_REQUEST),
DESC_ENTRY(ZEBRA_CLIENT_CLOSE_NOTIFY),
- DESC_ENTRY(ZEBRA_NHRP_NEIGH_ADDED),
- DESC_ENTRY(ZEBRA_NHRP_NEIGH_REMOVED),
- DESC_ENTRY(ZEBRA_NHRP_NEIGH_GET),
- DESC_ENTRY(ZEBRA_NHRP_NEIGH_REGISTER),
- DESC_ENTRY(ZEBRA_NHRP_NEIGH_UNREGISTER),
+ DESC_ENTRY(ZEBRA_NEIGH_ADDED),
+ DESC_ENTRY(ZEBRA_NEIGH_REMOVED),
+ DESC_ENTRY(ZEBRA_NEIGH_GET),
+ DESC_ENTRY(ZEBRA_NEIGH_REGISTER),
+ DESC_ENTRY(ZEBRA_NEIGH_UNREGISTER),
DESC_ENTRY(ZEBRA_NEIGH_IP_ADD),
DESC_ENTRY(ZEBRA_NEIGH_IP_DEL),
DESC_ENTRY(ZEBRA_CONFIGURE_ARP),
@@ -457,7 +460,9 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY(ZEBRA_TC_CLASS_ADD),
DESC_ENTRY(ZEBRA_TC_CLASS_DELETE),
DESC_ENTRY(ZEBRA_TC_FILTER_ADD),
- DESC_ENTRY(ZEBRA_TC_FILTER_DELETE)};
+ DESC_ENTRY(ZEBRA_TC_FILTER_DELETE),
+ DESC_ENTRY(ZEBRA_OPAQUE_NOTIFY)
+};
#undef DESC_ENTRY
static const struct zebra_desc_table unknown = {0, "unknown", '?'};
@@ -546,6 +551,8 @@ int proto_redistnum(int afi, const char *s)
return ZEBRA_ROUTE_KERNEL;
else if (strmatch(s, "connected"))
return ZEBRA_ROUTE_CONNECT;
+ else if (strmatch(s, "local"))
+ return ZEBRA_ROUTE_LOCAL;
else if (strmatch(s, "static"))
return ZEBRA_ROUTE_STATIC;
else if (strmatch(s, "rip"))
@@ -572,12 +579,16 @@ int proto_redistnum(int afi, const char *s)
return ZEBRA_ROUTE_SHARP;
else if (strmatch(s, "openfabric"))
return ZEBRA_ROUTE_OPENFABRIC;
+ else if (strmatch(s, "table-direct"))
+ return ZEBRA_ROUTE_TABLE_DIRECT;
}
if (afi == AFI_IP6) {
if (strmatch(s, "kernel"))
return ZEBRA_ROUTE_KERNEL;
else if (strmatch(s, "connected"))
return ZEBRA_ROUTE_CONNECT;
+ else if (strmatch(s, "local"))
+ return ZEBRA_ROUTE_LOCAL;
else if (strmatch(s, "static"))
return ZEBRA_ROUTE_STATIC;
else if (strmatch(s, "ripng"))
@@ -602,6 +613,8 @@ int proto_redistnum(int afi, const char *s)
return ZEBRA_ROUTE_SHARP;
else if (strmatch(s, "openfabric"))
return ZEBRA_ROUTE_OPENFABRIC;
+ else if (strmatch(s, "table-direct"))
+ return ZEBRA_ROUTE_TABLE_DIRECT;
}
return -1;
}
diff --git a/lib/log_vty.c b/lib/log_vty.c
index fc28ffc6fa..323b1b1d55 100644
--- a/lib/log_vty.c
+++ b/lib/log_vty.c
@@ -15,6 +15,7 @@
#include "lib/lib_errors.h"
#include "lib/printfrr.h"
#include "lib/systemd.h"
+#include "lib/vtysh_daemons.h"
#include "lib/log_vty_clippy.c"
@@ -34,18 +35,23 @@ static int log_cmdline_syslog_lvl = ZLOG_DISABLED;
static struct zlog_cfg_file zt_file_cmdline = {
.prio_min = ZLOG_DISABLED,
+ .ts_subsec = LOG_TIMESTAMP_PRECISION,
};
static struct zlog_cfg_file zt_file = {
.prio_min = ZLOG_DISABLED,
+ .ts_subsec = LOG_TIMESTAMP_PRECISION,
};
static struct zlog_cfg_filterfile zt_filterfile = {
- .parent = {
- .prio_min = ZLOG_DISABLED,
- },
+ .parent =
+ {
+ .prio_min = ZLOG_DISABLED,
+ .ts_subsec = LOG_TIMESTAMP_PRECISION,
+ },
};
static struct zlog_cfg_file zt_stdout_file = {
.prio_min = ZLOG_DISABLED,
+ .ts_subsec = LOG_TIMESTAMP_PRECISION,
};
static struct zlog_cfg_5424 zt_stdout_journald = {
.prio_min = ZLOG_DISABLED,
@@ -454,6 +460,70 @@ DEFUN (clear_log_cmdline,
return CMD_SUCCESS;
}
+/* Per-daemon log file config */
+DEFUN (config_log_dmn_file,
+ config_log_dmn_file_cmd,
+ "log daemon " DAEMONS_LIST " file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>$levelarg]",
+ "Logging control\n"
+ "Specific daemon\n"
+ DAEMONS_STR
+ "Logging to file\n"
+ "Logging filename\n"
+ LOG_LEVEL_DESC)
+{
+ int level = log_default_lvl;
+ int idx = 0;
+ const char *d_str;
+ const char *filename;
+ const char *levelarg = NULL;
+
+ d_str = argv[2]->text;
+
+ /* Ignore if not for this daemon */
+ if (!strmatch(d_str, frr_get_progname()))
+ return CMD_SUCCESS;
+
+ if (argv_find(argv, argc, "file", &idx))
+ filename = argv[idx + 1]->arg;
+ else
+ return CMD_SUCCESS;
+
+ if (argc > 5)
+ levelarg = argv[5]->text;
+
+ if (levelarg) {
+ level = log_level_match(levelarg);
+ if (level == ZLOG_DISABLED)
+ return CMD_ERR_NO_MATCH;
+ }
+ return set_log_file(&zt_file, vty, filename, level);
+}
+
+/* Per-daemon no log file */
+DEFUN (no_config_log_dmn_file,
+ no_config_log_dmn_file_cmd,
+ "no log daemon " DAEMONS_LIST " file [FILENAME [LEVEL]]",
+ NO_STR
+ "Logging control\n"
+ "Specific daemon\n"
+ DAEMONS_STR
+ "Cancel logging to file\n"
+ "Logging file name\n"
+ "Logging level\n")
+{
+ const char *d_str;
+
+ d_str = argv[3]->text;
+
+ /* Ignore if not for this daemon */
+ if (!strmatch(d_str, frr_get_progname()))
+ return CMD_SUCCESS;
+
+ zt_file.prio_min = ZLOG_DISABLED;
+ zlog_file_set_other(&zt_file);
+ return CMD_SUCCESS;
+}
+
DEFPY (config_log_file,
config_log_file_cmd,
"log file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>$levelarg]",
@@ -832,6 +902,8 @@ void log_config_write(struct vty *vty)
vty_out(vty, "no log error-category\n");
if (!zlog_get_prefix_xid())
vty_out(vty, "no log unique-id\n");
+ if (zlog_get_immediate_mode())
+ vty_out(vty, "log immediate-mode\n");
if (logmsgs_with_persist_bt) {
struct xrefdata *xrd;
@@ -852,12 +924,24 @@ void log_config_write(struct vty *vty)
}
}
+static int log_vty_fini(void)
+{
+ if (zt_file_cmdline.filename)
+ zlog_file_fini(&zt_file_cmdline);
+ if (zt_file.filename)
+ zlog_file_fini(&zt_file);
+ return 0;
+}
+
+
static int log_vty_init(const char *progname, const char *protoname,
unsigned short instance, uid_t uid, gid_t gid)
{
zlog_progname = progname;
zlog_protoname = protoname;
+ hook_register(zlog_fini, log_vty_fini);
+
zlog_set_prefix_ec(true);
zlog_set_prefix_xid(true);
@@ -887,6 +971,8 @@ void log_cmd_init(void)
install_element(CONFIG_NODE, &config_log_monitor_cmd);
install_element(CONFIG_NODE, &no_config_log_monitor_cmd);
install_element(CONFIG_NODE, &config_log_file_cmd);
+ install_element(CONFIG_NODE, &config_log_dmn_file_cmd);
+ install_element(CONFIG_NODE, &no_config_log_dmn_file_cmd);
install_element(CONFIG_NODE, &no_config_log_file_cmd);
install_element(CONFIG_NODE, &config_log_syslog_cmd);
install_element(CONFIG_NODE, &no_config_log_syslog_cmd);
diff --git a/lib/mgmt.proto b/lib/mgmt.proto
index 8a11ff0fa5..01a99ab63b 100644
--- a/lib/mgmt.proto
+++ b/lib/mgmt.proto
@@ -55,7 +55,10 @@ message YangData {
enum CfgDataReqType {
REQ_TYPE_NONE = 0;
SET_DATA = 1;
- DELETE_DATA = 2;
+ REMOVE_DATA = 2;
+ CREATE_DATA = 3;
+ DELETE_DATA = 4;
+ REPLACE_DATA = 5;
}
message YangCfgDataReq {
@@ -73,8 +76,9 @@ message YangGetDataReq {
//
message BeSubscribeReq {
required string client_name = 1;
- required bool subscribe_xpaths = 2;
- repeated string xpath_reg = 3;
+ repeated string config_xpaths = 2;
+ repeated string oper_xpaths = 3;
+ repeated string notif_xpaths = 4;
}
message BeSubscribeReply {
@@ -94,16 +98,14 @@ message BeTxnReply {
message BeCfgDataCreateReq {
required uint64 txn_id = 1;
- required uint64 batch_id = 2;
- repeated YangCfgDataReq data_req = 3;
- required bool end_of_data = 4;
+ repeated YangCfgDataReq data_req = 2;
+ required bool end_of_data = 3;
}
message BeCfgDataCreateReply {
required uint64 txn_id = 1;
- required uint64 batch_id = 2;
- required bool success = 3;
- optional string error_if_any = 4;
+ required bool success = 2;
+ optional string error_if_any = 3;
}
message BeCfgDataApplyReq {
@@ -112,15 +114,8 @@ message BeCfgDataApplyReq {
message BeCfgDataApplyReply {
required uint64 txn_id = 1;
- repeated uint64 batch_ids = 2;
- required bool success = 3;
- optional string error_if_any = 4;
-}
-
-message BeOperDataGetReq {
- required uint64 txn_id = 1;
- required uint64 batch_id = 2;
- repeated YangGetDataReq data = 3;
+ required bool success = 2;
+ optional string error_if_any = 3;
}
message YangDataReply {
@@ -128,36 +123,6 @@ message YangDataReply {
required int64 next_indx = 2;
}
-message BeOperDataGetReply {
- required uint64 txn_id = 1;
- required uint64 batch_id = 2;
- required bool success = 3;
- optional string error = 4;
- optional YangDataReply data = 5;
-}
-
-message BeOperDataNotify {
- required YangDataReply data = 5;
-}
-
-message BeConfigCmdReq {
- required string cmd = 1;
-}
-
-message BeConfigCmdReply {
- required bool success = 1;
- required string error_if_any = 2;
-}
-
-message BeShowCmdReq {
- required string cmd = 1;
-}
-
-message BeShowCmdReply {
- required bool success = 1;
- required string cmd_ouput = 2;
-}
-
//
// Any message on the MGMTD Backend Interface.
//
@@ -171,13 +136,6 @@ message BeMessage {
BeCfgDataCreateReply cfg_data_reply = 7;
BeCfgDataApplyReq cfg_apply_req = 8;
BeCfgDataApplyReply cfg_apply_reply = 9;
- BeOperDataGetReq get_req = 10;
- BeOperDataGetReply get_reply = 11;
- BeOperDataNotify notify_data = 12;
- BeConfigCmdReq cfg_cmd_req = 13;
- BeConfigCmdReply cfg_cmd_reply = 14;
- BeShowCmdReq show_cmd_req = 15;
- BeShowCmdReply show_cmd_reply = 16;
}
}
@@ -243,7 +201,8 @@ message FeSetConfigReply {
required DatastoreId ds_id = 2;
required uint64 req_id = 3;
required bool success = 4;
- optional string error_if_any = 5;
+ required bool implicit_commit = 5;
+ optional string error_if_any = 6;
}
message FeCommitConfigReq {
@@ -266,36 +225,22 @@ message FeCommitConfigReply {
optional string error_if_any = 8;
}
-message FeGetConfigReq {
- required uint64 session_id = 1;
- required DatastoreId ds_id = 2;
- required uint64 req_id = 3;
- repeated YangGetDataReq data = 4;
-}
-
-message FeGetConfigReply {
- required uint64 session_id = 1;
- required DatastoreId ds_id = 2;
- required uint64 req_id = 3;
- required bool success = 4;
- optional string error_if_any = 5;
- optional YangDataReply data = 6;
-}
-
-message FeGetDataReq {
+message FeGetReq {
required uint64 session_id = 1;
- required DatastoreId ds_id = 2;
- required uint64 req_id = 3;
- repeated YangGetDataReq data = 4;
+ required bool config = 2;
+ required DatastoreId ds_id = 3;
+ required uint64 req_id = 4;
+ repeated YangGetDataReq data = 5;
}
-message FeGetDataReply {
+message FeGetReply {
required uint64 session_id = 1;
- required DatastoreId ds_id = 2;
- required uint64 req_id = 3;
- required bool success = 4;
- optional string error_if_any = 5;
- optional YangDataReply data = 6;
+ required bool config = 2;
+ required DatastoreId ds_id = 3;
+ required uint64 req_id = 4;
+ required bool success = 5;
+ optional string error_if_any = 6;
+ optional YangDataReply data = 7;
}
message FeNotifyDataReq {
@@ -321,10 +266,8 @@ message FeMessage {
FeSetConfigReply setcfg_reply = 8;
FeCommitConfigReq commcfg_req = 9;
FeCommitConfigReply commcfg_reply = 10;
- FeGetConfigReq getcfg_req = 11;
- FeGetConfigReply getcfg_reply = 12;
- FeGetDataReq getdata_req = 13;
- FeGetDataReply getdata_reply = 14;
+ FeGetReq get_req = 11;
+ FeGetReply get_reply = 12;
FeNotifyDataReq notify_data_req = 15;
FeRegisterNotifyReq regnotify_req = 16;
}
diff --git a/lib/mgmt_be_client.c b/lib/mgmt_be_client.c
index 7437eedfc7..f483d48d8d 100644
--- a/lib/mgmt_be_client.c
+++ b/lib/mgmt_be_client.c
@@ -6,38 +6,32 @@
*/
#include <zebra.h>
+#include "debug.h"
+#include "compiler.h"
+#include "darr.h"
#include "libfrr.h"
-#include "mgmtd/mgmt.h"
+#include "lib_errors.h"
#include "mgmt_be_client.h"
#include "mgmt_msg.h"
+#include "mgmt_msg_native.h"
#include "mgmt_pb.h"
#include "network.h"
+#include "northbound.h"
#include "stream.h"
#include "sockopt.h"
+#include "northbound_cli.h"
-#ifdef REDIRECT_DEBUG_TO_STDERR
-#define MGMTD_BE_CLIENT_DBG(fmt, ...) \
- fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
-#define MGMTD_BE_CLIENT_ERR(fmt, ...) \
- fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
-#else /* REDIRECT_DEBUG_TO_STDERR */
-#define MGMTD_BE_CLIENT_DBG(fmt, ...) \
- do { \
- if (mgmt_debug_be_client) \
- zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__); \
- } while (0)
-#define MGMTD_BE_CLIENT_ERR(fmt, ...) \
- zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
-#endif /* REDIRECT_DEBUG_TO_STDERR */
-
-DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_BATCH,
- "MGMTD backend transaction batch data");
-DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_TXN, "MGMTD backend transaction data");
+#include "lib/mgmt_be_client_clippy.c"
+
+DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_CLIENT, "backend client");
+DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_CLIENT_NAME, "backend client name");
+DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_BATCH, "backend transaction batch data");
+DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_TXN, "backend transaction data");
+DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_GT_CB_ARGS, "backend get-tree cb args");
enum mgmt_be_txn_event {
MGMTD_BE_TXN_PROC_SETCFG = 1,
MGMTD_BE_TXN_PROC_GETCFG,
- MGMTD_BE_TXN_PROC_GETDATA
};
struct mgmt_be_set_cfg_req {
@@ -45,24 +39,20 @@ struct mgmt_be_set_cfg_req {
uint16_t num_cfg_changes;
};
-struct mgmt_be_get_data_req {
- char *xpaths[MGMTD_MAX_NUM_DATA_REQ_IN_BATCH];
- uint16_t num_xpaths;
-};
-
struct mgmt_be_txn_req {
enum mgmt_be_txn_event event;
union {
struct mgmt_be_set_cfg_req set_cfg;
- struct mgmt_be_get_data_req get_data;
} req;
};
+struct be_oper_iter_arg {
+ struct lyd_node *root; /* the tree we are building */
+ struct lyd_node *hint; /* last node added */
+};
+
PREDECL_LIST(mgmt_be_batches);
struct mgmt_be_batch_ctx {
- /* Batch-Id as assigned by MGMTD */
- uint64_t batch_id;
-
struct mgmt_be_txn_req txn_req;
uint32_t flags;
@@ -73,8 +63,6 @@ struct mgmt_be_batch_ctx {
#define MGMTD_BE_TXN_FLAGS_CFG_APPLIED (1U << 1)
DECLARE_LIST(mgmt_be_batches, struct mgmt_be_batch_ctx, list_linkage);
-struct mgmt_be_client_ctx;
-
PREDECL_LIST(mgmt_be_txns);
struct mgmt_be_txn_ctx {
/* Txn-Id as assigned by MGMTD */
@@ -82,7 +70,7 @@ struct mgmt_be_txn_ctx {
uint32_t flags;
struct mgmt_be_client_txn_ctx client_data;
- struct mgmt_be_client_ctx *client_ctx;
+ struct mgmt_be_client *client;
/* List of batches belonging to this transaction */
struct mgmt_be_batches_head cfg_batches;
@@ -103,23 +91,14 @@ DECLARE_LIST(mgmt_be_txns, struct mgmt_be_txn_ctx, list_linkage);
#define FOREACH_BE_APPLY_BATCH_IN_LIST(txn, batch) \
frr_each_safe (mgmt_be_batches, &(txn)->apply_cfgs, (batch))
-struct mgmt_be_client_ctx {
- int conn_fd;
- struct event_loop *tm;
- struct event *conn_retry_tmr;
- struct event *conn_read_ev;
- struct event *conn_write_ev;
- struct event *conn_writes_on;
- struct event *msg_proc_ev;
- uint32_t flags;
+struct mgmt_be_client {
+ struct msg_client client;
- struct mgmt_msg_state mstate;
+ char *name;
struct nb_config *candidate_config;
struct nb_config *running_config;
- unsigned long num_batch_find;
- unsigned long avg_batch_find_tm;
unsigned long num_edit_nb_cfg;
unsigned long avg_edit_nb_cfg_tm;
unsigned long num_prep_nb_cfg;
@@ -128,89 +107,49 @@ struct mgmt_be_client_ctx {
unsigned long avg_apply_nb_cfg_tm;
struct mgmt_be_txns_head txn_head;
- struct mgmt_be_client_params client_params;
-};
-#define MGMTD_BE_CLIENT_FLAGS_WRITES_OFF (1U << 0)
+ struct mgmt_be_client_cbs cbs;
+ uintptr_t user_data;
+};
#define FOREACH_BE_TXN_IN_LIST(client_ctx, txn) \
frr_each_safe (mgmt_be_txns, &(client_ctx)->txn_head, (txn))
-static bool mgmt_debug_be_client;
-
-static struct mgmt_be_client_ctx mgmt_be_client_ctx = {
- .conn_fd = -1,
+struct debug mgmt_dbg_be_client = {
+ .desc = "Management backend client operations"
};
-const char *mgmt_be_client_names[MGMTD_BE_CLIENT_ID_MAX + 1] = {
-#ifdef HAVE_STATICD
- [MGMTD_BE_CLIENT_ID_STATICD] = "staticd",
-#endif
- [MGMTD_BE_CLIENT_ID_MAX] = "Unknown/Invalid",
-};
+/* NOTE: only one client per proc for now. */
+static struct mgmt_be_client *__be_client;
-/* Forward declarations */
-static void
-mgmt_be_client_register_event(struct mgmt_be_client_ctx *client_ctx,
- enum mgmt_be_event event);
-static void
-mgmt_be_client_schedule_conn_retry(struct mgmt_be_client_ctx *client_ctx,
- unsigned long intvl_secs);
-static int mgmt_be_client_send_msg(struct mgmt_be_client_ctx *client_ctx,
- Mgmtd__BeMessage *be_msg);
-
-static void
-mgmt_be_server_disconnect(struct mgmt_be_client_ctx *client_ctx,
- bool reconnect)
+static int be_client_send_native_msg(struct mgmt_be_client *client_ctx,
+ void *msg, size_t len,
+ bool short_circuit_ok)
{
- /* Notify client through registered callback (if any) */
- if (client_ctx->client_params.client_connect_notify)
- (void)(*client_ctx->client_params.client_connect_notify)(
- (uintptr_t)client_ctx,
- client_ctx->client_params.user_data, false);
-
- if (client_ctx->conn_fd != -1) {
- close(client_ctx->conn_fd);
- client_ctx->conn_fd = -1;
- }
-
- if (reconnect)
- mgmt_be_client_schedule_conn_retry(
- client_ctx,
- client_ctx->client_params.conn_retry_intvl_sec);
+ return msg_conn_send_msg(&client_ctx->client.conn,
+ MGMT_MSG_VERSION_NATIVE, msg, len, NULL,
+ short_circuit_ok);
}
-static struct mgmt_be_batch_ctx *
-mgmt_be_find_batch_by_id(struct mgmt_be_txn_ctx *txn,
- uint64_t batch_id)
+static int mgmt_be_client_send_msg(struct mgmt_be_client *client_ctx,
+ Mgmtd__BeMessage *be_msg)
{
- struct mgmt_be_batch_ctx *batch = NULL;
-
- FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) {
- if (batch->batch_id == batch_id)
- return batch;
- }
-
- return NULL;
+ return msg_conn_send_msg(
+ &client_ctx->client.conn, MGMT_MSG_VERSION_PROTOBUF, be_msg,
+ mgmtd__be_message__get_packed_size(be_msg),
+ (size_t(*)(void *, void *))mgmtd__be_message__pack, false);
}
static struct mgmt_be_batch_ctx *
-mgmt_be_batch_create(struct mgmt_be_txn_ctx *txn, uint64_t batch_id)
+mgmt_be_batch_create(struct mgmt_be_txn_ctx *txn)
{
struct mgmt_be_batch_ctx *batch = NULL;
- batch = mgmt_be_find_batch_by_id(txn, batch_id);
- if (!batch) {
- batch = XCALLOC(MTYPE_MGMTD_BE_BATCH,
- sizeof(struct mgmt_be_batch_ctx));
- assert(batch);
+ batch = XCALLOC(MTYPE_MGMTD_BE_BATCH, sizeof(struct mgmt_be_batch_ctx));
- batch->batch_id = batch_id;
- mgmt_be_batches_add_tail(&txn->cfg_batches, batch);
+ mgmt_be_batches_add_tail(&txn->cfg_batches, batch);
- MGMTD_BE_CLIENT_DBG("Added new batch 0x%llx to transaction",
- (unsigned long long)batch_id);
- }
+ debug_be_client("Added new batch to transaction");
return batch;
}
@@ -254,46 +193,47 @@ static void mgmt_be_cleanup_all_batches(struct mgmt_be_txn_ctx *txn)
}
static struct mgmt_be_txn_ctx *
-mgmt_be_find_txn_by_id(struct mgmt_be_client_ctx *client_ctx,
- uint64_t txn_id)
+mgmt_be_find_txn_by_id(struct mgmt_be_client *client_ctx, uint64_t txn_id,
+ bool warn)
{
struct mgmt_be_txn_ctx *txn = NULL;
- FOREACH_BE_TXN_IN_LIST (client_ctx, txn) {
+ FOREACH_BE_TXN_IN_LIST (client_ctx, txn)
if (txn->txn_id == txn_id)
return txn;
- }
+ if (warn)
+ log_err_be_client("client %s unkonwn txn-id: %" PRIu64,
+ client_ctx->name, txn_id);
return NULL;
}
static struct mgmt_be_txn_ctx *
-mgmt_be_txn_create(struct mgmt_be_client_ctx *client_ctx,
- uint64_t txn_id)
+mgmt_be_txn_create(struct mgmt_be_client *client_ctx, uint64_t txn_id)
{
struct mgmt_be_txn_ctx *txn = NULL;
- txn = mgmt_be_find_txn_by_id(client_ctx, txn_id);
- if (!txn) {
- txn = XCALLOC(MTYPE_MGMTD_BE_TXN,
- sizeof(struct mgmt_be_txn_ctx));
- assert(txn);
+ txn = mgmt_be_find_txn_by_id(client_ctx, txn_id, false);
+ if (txn) {
+ log_err_be_client("Can't create existing txn-id: %" PRIu64,
+ txn_id);
+ return NULL;
+ }
- txn->txn_id = txn_id;
- txn->client_ctx = client_ctx;
- mgmt_be_batches_init(&txn->cfg_batches);
- mgmt_be_batches_init(&txn->apply_cfgs);
- mgmt_be_txns_add_tail(&client_ctx->txn_head, txn);
+ txn = XCALLOC(MTYPE_MGMTD_BE_TXN, sizeof(struct mgmt_be_txn_ctx));
+ txn->txn_id = txn_id;
+ txn->client = client_ctx;
+ mgmt_be_batches_init(&txn->cfg_batches);
+ mgmt_be_batches_init(&txn->apply_cfgs);
+ mgmt_be_txns_add_tail(&client_ctx->txn_head, txn);
- MGMTD_BE_CLIENT_DBG("Added new transaction 0x%llx",
- (unsigned long long)txn_id);
- }
+ debug_be_client("Created new txn-id: %" PRIu64, txn_id);
return txn;
}
-static void mgmt_be_txn_delete(struct mgmt_be_client_ctx *client_ctx,
- struct mgmt_be_txn_ctx **txn)
+static void mgmt_be_txn_delete(struct mgmt_be_client *client_ctx,
+ struct mgmt_be_txn_ctx **txn)
{
char err_msg[] = "MGMT Transaction Delete";
@@ -313,12 +253,10 @@ static void mgmt_be_txn_delete(struct mgmt_be_client_ctx *client_ctx,
* CFGDATA_CREATE_REQs. But first notify the client
* about the transaction delete.
*/
- if (client_ctx->client_params.txn_notify)
- (void)(*client_ctx->client_params
- .txn_notify)(
- (uintptr_t)client_ctx,
- client_ctx->client_params.user_data,
- &(*txn)->client_data, true);
+ if (client_ctx->cbs.txn_notify)
+ (void)(*client_ctx->cbs.txn_notify)(client_ctx,
+ client_ctx->user_data,
+ &(*txn)->client_data, true);
mgmt_be_cleanup_all_batches(*txn);
if ((*txn)->nb_txn)
@@ -329,8 +267,7 @@ static void mgmt_be_txn_delete(struct mgmt_be_client_ctx *client_ctx,
*txn = NULL;
}
-static void
-mgmt_be_cleanup_all_txns(struct mgmt_be_client_ctx *client_ctx)
+static void mgmt_be_cleanup_all_txns(struct mgmt_be_client *client_ctx)
{
struct mgmt_be_txn_ctx *txn = NULL;
@@ -339,9 +276,86 @@ mgmt_be_cleanup_all_txns(struct mgmt_be_client_ctx *client_ctx)
}
}
-static int mgmt_be_send_txn_reply(struct mgmt_be_client_ctx *client_ctx,
- uint64_t txn_id, bool create,
- bool success)
+
+/**
+ * Send an error back to MGMTD using native messaging.
+ *
+ * Args:
+ * client: the BE client.
+ * txn_id: the txn_id this error pertains to.
+ * short_circuit_ok: True if OK to short-circuit the call.
+ * error: An integer error value.
+ * errfmt: An error format string (i.e., printfrr)
+ * ...: args for use by the `errfmt` format string.
+ *
+ * Return:
+ * the return value from the underlying send message function.
+ */
+static int be_client_send_error(struct mgmt_be_client *client, uint64_t txn_id,
+ uint64_t req_id, bool short_circuit_ok,
+ int16_t error, const char *errfmt, ...)
+ PRINTFRR(6, 7);
+
+static int be_client_send_error(struct mgmt_be_client *client, uint64_t txn_id,
+ uint64_t req_id, bool short_circuit_ok,
+ int16_t error, const char *errfmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, errfmt);
+ ret = vmgmt_msg_native_send_error(&client->client.conn, txn_id, req_id,
+ short_circuit_ok, error, errfmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+static int mgmt_be_send_notification(void *__be_client, const char *xpath,
+ const struct lyd_node *tree)
+{
+ struct mgmt_be_client *client = __be_client;
+ struct mgmt_msg_notify_data *msg = NULL;
+ LYD_FORMAT format = LYD_JSON;
+ uint8_t **darrp;
+ LY_ERR err;
+ int ret = 0;
+
+ assert(tree);
+
+ debug_be_client("%s: sending YANG notification: %s", __func__,
+ tree->schema->name);
+ /*
+ * Allocate a message and append the data to it using `format`
+ */
+ msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_notify_data, 0,
+ MTYPE_MSG_NATIVE_NOTIFY);
+ msg->code = MGMT_MSG_CODE_NOTIFY;
+ msg->result_type = format;
+
+ mgmt_msg_native_xpath_encode(msg, xpath);
+
+ darrp = mgmt_msg_native_get_darrp(msg);
+ err = yang_print_tree_append(darrp, tree, format,
+ (LYD_PRINT_SHRINK | LYD_PRINT_WD_EXPLICIT |
+ LYD_PRINT_WITHSIBLINGS));
+ if (err) {
+ flog_err(EC_LIB_LIBYANG,
+ "%s: error creating notification data: %s", __func__,
+ ly_strerrcode(err));
+ ret = 1;
+ goto done;
+ }
+
+ (void)be_client_send_native_msg(client, msg,
+ mgmt_msg_native_get_msg_len(msg), false);
+done:
+ mgmt_msg_native_free_msg(msg);
+ return ret;
+}
+
+static int mgmt_be_send_txn_reply(struct mgmt_be_client *client_ctx,
+ uint64_t txn_id, bool create)
{
Mgmtd__BeMessage be_msg;
Mgmtd__BeTxnReply txn_reply;
@@ -349,80 +363,56 @@ static int mgmt_be_send_txn_reply(struct mgmt_be_client_ctx *client_ctx,
mgmtd__be_txn_reply__init(&txn_reply);
txn_reply.create = create;
txn_reply.txn_id = txn_id;
- txn_reply.success = success;
+ txn_reply.success = true;
mgmtd__be_message__init(&be_msg);
be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_TXN_REPLY;
be_msg.txn_reply = &txn_reply;
- MGMTD_BE_CLIENT_DBG(
- "Sending TXN_REPLY message to MGMTD for txn 0x%llx",
- (unsigned long long)txn_id);
+ debug_be_client("Sending TXN_REPLY txn-id %" PRIu64, txn_id);
return mgmt_be_client_send_msg(client_ctx, &be_msg);
}
-static int mgmt_be_process_txn_req(struct mgmt_be_client_ctx *client_ctx,
- uint64_t txn_id, bool create)
+static int mgmt_be_process_txn_req(struct mgmt_be_client *client_ctx,
+ uint64_t txn_id, bool create)
{
struct mgmt_be_txn_ctx *txn;
- txn = mgmt_be_find_txn_by_id(client_ctx, txn_id);
if (create) {
- if (txn) {
- /*
- * Transaction with same txn-id already exists.
- * Should not happen under any circumstances.
- */
- MGMTD_BE_CLIENT_ERR(
- "Transaction 0x%llx already exists!!!",
- (unsigned long long)txn_id);
- mgmt_be_send_txn_reply(client_ctx, txn_id, create,
- false);
- }
+ debug_be_client("Creating new txn-id %" PRIu64, txn_id);
- MGMTD_BE_CLIENT_DBG("Created new transaction 0x%llx",
- (unsigned long long)txn_id);
txn = mgmt_be_txn_create(client_ctx, txn_id);
+ if (!txn)
+ goto failed;
- if (client_ctx->client_params.txn_notify)
- (void)(*client_ctx->client_params
- .txn_notify)(
- (uintptr_t)client_ctx,
- client_ctx->client_params.user_data,
- &txn->client_data, false);
+ if (client_ctx->cbs.txn_notify)
+ (*client_ctx->cbs.txn_notify)(client_ctx,
+ client_ctx->user_data,
+ &txn->client_data, false);
} else {
- if (!txn) {
- /*
- * Transaction with same txn-id does not exists.
- * Return sucess anyways.
- */
- MGMTD_BE_CLIENT_DBG(
- "Transaction to delete 0x%llx does NOT exists!!!",
- (unsigned long long)txn_id);
- } else {
- MGMTD_BE_CLIENT_DBG("Delete transaction 0x%llx",
- (unsigned long long)txn_id);
+ debug_be_client("Deleting txn-id: %" PRIu64, txn_id);
+ txn = mgmt_be_find_txn_by_id(client_ctx, txn_id, false);
+ if (txn)
mgmt_be_txn_delete(client_ctx, &txn);
- }
}
- mgmt_be_send_txn_reply(client_ctx, txn_id, create, true);
+ return mgmt_be_send_txn_reply(client_ctx, txn_id, create);
- return 0;
+failed:
+ msg_conn_disconnect(&client_ctx->client.conn, true);
+ return -1;
}
-static int
-mgmt_be_send_cfgdata_create_reply(struct mgmt_be_client_ctx *client_ctx,
- uint64_t txn_id, uint64_t batch_id,
- bool success, const char *error_if_any)
+static int mgmt_be_send_cfgdata_create_reply(struct mgmt_be_client *client_ctx,
+ uint64_t txn_id, bool success,
+ const char *error_if_any)
{
Mgmtd__BeMessage be_msg;
Mgmtd__BeCfgDataCreateReply cfgdata_reply;
mgmtd__be_cfg_data_create_reply__init(&cfgdata_reply);
cfgdata_reply.txn_id = (uint64_t)txn_id;
- cfgdata_reply.batch_id = (uint64_t)batch_id;
cfgdata_reply.success = success;
if (error_if_any)
cfgdata_reply.error_if_any = (char *)error_if_any;
@@ -431,9 +421,7 @@ mgmt_be_send_cfgdata_create_reply(struct mgmt_be_client_ctx *client_ctx,
be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY;
be_msg.cfg_data_reply = &cfgdata_reply;
- MGMTD_BE_CLIENT_DBG(
- "Sending CFGDATA_CREATE_REPLY message to MGMTD for txn 0x%llx batch 0x%llx",
- (unsigned long long)txn_id, (unsigned long long)batch_id);
+ debug_be_client("Sending CFGDATA_CREATE_REPLY txn-id: %" PRIu64, txn_id);
return mgmt_be_client_send_msg(client_ctx, &be_msg);
}
@@ -442,11 +430,10 @@ static void mgmt_be_txn_cfg_abort(struct mgmt_be_txn_ctx *txn)
{
char errmsg[BUFSIZ] = {0};
- assert(txn && txn->client_ctx);
+ assert(txn && txn->client);
if (txn->nb_txn) {
- MGMTD_BE_CLIENT_ERR(
- "Aborting configurations after prep for Txn 0x%llx",
- (unsigned long long)txn->txn_id);
+ log_err_be_client("Aborting configs after prep for txn-id: %" PRIu64,
+ txn->txn_id);
nb_candidate_commit_abort(txn->nb_txn, errmsg, sizeof(errmsg));
txn->nb_txn = 0;
}
@@ -457,16 +444,15 @@ static void mgmt_be_txn_cfg_abort(struct mgmt_be_txn_ctx *txn)
* This is one txn ctx but the candidate_config is per client ctx, how
* does that work?
*/
- MGMTD_BE_CLIENT_DBG(
- "Reset candidate configurations after abort of Txn 0x%llx",
- (unsigned long long)txn->txn_id);
- nb_config_replace(txn->client_ctx->candidate_config,
- txn->client_ctx->running_config, true);
+ debug_be_client("Reset candidate configurations after abort of txn-id: %" PRIu64,
+ txn->txn_id);
+ nb_config_replace(txn->client->candidate_config,
+ txn->client->running_config, true);
}
static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn)
{
- struct mgmt_be_client_ctx *client_ctx;
+ struct mgmt_be_client *client_ctx;
struct mgmt_be_txn_req *txn_req = NULL;
struct nb_context nb_ctx = {0};
struct timeval edit_nb_cfg_start;
@@ -479,18 +465,17 @@ static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn)
bool error;
char err_buf[BUFSIZ];
size_t num_processed;
- bool debug_be = mgmt_debug_be_client;
int err;
- assert(txn && txn->client_ctx);
- client_ctx = txn->client_ctx;
+ assert(txn && txn->client);
+ client_ctx = txn->client;
num_processed = 0;
FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) {
txn_req = &batch->txn_req;
error = false;
nb_ctx.client = NB_CLIENT_CLI;
- nb_ctx.user = (void *)client_ctx->client_params.user_data;
+ nb_ctx.user = (void *)client_ctx->user_data;
if (!txn->nb_txn) {
/*
@@ -499,33 +484,28 @@ static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn)
* interested in validating it.
*/
error = false;
- if (debug_be)
- gettimeofday(&edit_nb_cfg_start, NULL);
+
+ gettimeofday(&edit_nb_cfg_start, NULL);
nb_candidate_edit_config_changes(
client_ctx->candidate_config,
txn_req->req.set_cfg.cfg_changes,
(size_t)txn_req->req.set_cfg.num_cfg_changes,
- NULL, NULL, 0, err_buf, sizeof(err_buf),
- &error);
+ NULL, true, err_buf, sizeof(err_buf), &error);
if (error) {
err_buf[sizeof(err_buf) - 1] = 0;
- MGMTD_BE_CLIENT_ERR(
- "Failed to update configs for Txn %llx Batch %llx to Candidate! Err: '%s'",
- (unsigned long long)txn->txn_id,
- (unsigned long long)batch->batch_id,
- err_buf);
+ log_err_be_client("Failed to update configs for txn-id: %" PRIu64
+ " to candidate, err: '%s'",
+ txn->txn_id, err_buf);
return -1;
}
- if (debug_be) {
- gettimeofday(&edit_nb_cfg_end, NULL);
- edit_nb_cfg_tm = timeval_elapsed(
- edit_nb_cfg_end, edit_nb_cfg_start);
- client_ctx->avg_edit_nb_cfg_tm =
- ((client_ctx->avg_edit_nb_cfg_tm
- * client_ctx->num_edit_nb_cfg)
- + edit_nb_cfg_tm)
- / (client_ctx->num_edit_nb_cfg + 1);
- }
+ gettimeofday(&edit_nb_cfg_end, NULL);
+ edit_nb_cfg_tm = timeval_elapsed(edit_nb_cfg_end,
+ edit_nb_cfg_start);
+ client_ctx->avg_edit_nb_cfg_tm =
+ ((client_ctx->avg_edit_nb_cfg_tm *
+ client_ctx->num_edit_nb_cfg) +
+ edit_nb_cfg_tm) /
+ (client_ctx->num_edit_nb_cfg + 1);
client_ctx->num_edit_nb_cfg++;
}
@@ -539,9 +519,9 @@ static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn)
* Now prepare all the batches we have applied in one go.
*/
nb_ctx.client = NB_CLIENT_CLI;
- nb_ctx.user = (void *)client_ctx->client_params.user_data;
- if (debug_be)
- gettimeofday(&prep_nb_cfg_start, NULL);
+ nb_ctx.user = (void *)client_ctx->user_data;
+
+ gettimeofday(&prep_nb_cfg_start, NULL);
err = nb_candidate_commit_prepare(nb_ctx, client_ctx->candidate_config,
"MGMTD Backend Txn", &txn->nb_txn,
#ifdef MGMTD_LOCAL_VALIDATIONS_ENABLED
@@ -553,38 +533,29 @@ static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn)
if (err != NB_OK) {
err_buf[sizeof(err_buf) - 1] = 0;
if (err == NB_ERR_VALIDATION)
- MGMTD_BE_CLIENT_ERR(
- "Failed to validate configs for Txn %llx %u Batches! Err: '%s'",
- (unsigned long long)txn->txn_id,
- (uint32_t)num_processed, err_buf);
+ log_err_be_client("Failed to validate configs txn-id: %" PRIu64
+ " %zu batches, err: '%s'",
+ txn->txn_id, num_processed, err_buf);
else
- MGMTD_BE_CLIENT_ERR(
- "Failed to prepare configs for Txn %llx, %u Batches! Err: '%s'",
- (unsigned long long)txn->txn_id,
- (uint32_t)num_processed, err_buf);
+ log_err_be_client("Failed to prepare configs for txn-id: %" PRIu64
+ " %zu batches, err: '%s'",
+ txn->txn_id, num_processed, err_buf);
error = true;
SET_FLAG(txn->flags, MGMTD_BE_TXN_FLAGS_CFGPREP_FAILED);
} else
- MGMTD_BE_CLIENT_DBG(
- "Prepared configs for Txn %llx, %u Batches! successfully!",
- (unsigned long long)txn->txn_id,
- (uint32_t)num_processed);
- if (debug_be) {
- gettimeofday(&prep_nb_cfg_end, NULL);
- prep_nb_cfg_tm =
- timeval_elapsed(prep_nb_cfg_end, prep_nb_cfg_start);
- client_ctx->avg_prep_nb_cfg_tm =
- ((client_ctx->avg_prep_nb_cfg_tm
- * client_ctx->num_prep_nb_cfg)
- + prep_nb_cfg_tm)
- / (client_ctx->num_prep_nb_cfg + 1);
- }
+ debug_be_client("Prepared configs for txn-id: %" PRIu64
+ " %zu batches",
+ txn->txn_id, num_processed);
+
+ gettimeofday(&prep_nb_cfg_end, NULL);
+ prep_nb_cfg_tm = timeval_elapsed(prep_nb_cfg_end, prep_nb_cfg_start);
+ client_ctx->avg_prep_nb_cfg_tm = ((client_ctx->avg_prep_nb_cfg_tm *
+ client_ctx->num_prep_nb_cfg) +
+ prep_nb_cfg_tm) /
+ (client_ctx->num_prep_nb_cfg + 1);
client_ctx->num_prep_nb_cfg++;
FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) {
- mgmt_be_send_cfgdata_create_reply(
- client_ctx, txn->txn_id, batch->batch_id,
- error ? false : true, error ? err_buf : NULL);
if (!error) {
SET_FLAG(batch->flags,
MGMTD_BE_BATCH_FLAGS_CFG_PREPARED);
@@ -593,12 +564,12 @@ static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn)
}
}
- if (debug_be)
- MGMTD_BE_CLIENT_DBG(
- "Avg-nb-edit-duration %lu uSec, nb-prep-duration %lu (avg: %lu) uSec, batch size %u",
+ mgmt_be_send_cfgdata_create_reply(client_ctx, txn->txn_id,
+ error ? false : true, error ? err_buf : NULL);
+
+ debug_be_client("Avg-nb-edit-duration %lu uSec, nb-prep-duration %lu (avg: %lu) uSec, batch size %u",
client_ctx->avg_edit_nb_cfg_tm, prep_nb_cfg_tm,
- client_ctx->avg_prep_nb_cfg_tm,
- (uint32_t)num_processed);
+ client_ctx->avg_prep_nb_cfg_tm, (uint32_t)num_processed);
if (error)
mgmt_be_txn_cfg_abort(txn);
@@ -609,40 +580,49 @@ static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn)
/*
* Process all CFG_DATA_REQs received so far and prepare them all in one go.
*/
-static int
-mgmt_be_update_setcfg_in_batch(struct mgmt_be_client_ctx *client_ctx,
- struct mgmt_be_txn_ctx *txn,
- uint64_t batch_id,
- Mgmtd__YangCfgDataReq * cfg_req[],
- int num_req)
+static int mgmt_be_update_setcfg_in_batch(struct mgmt_be_client *client_ctx,
+ struct mgmt_be_txn_ctx *txn,
+ Mgmtd__YangCfgDataReq *cfg_req[],
+ int num_req)
{
struct mgmt_be_batch_ctx *batch = NULL;
struct mgmt_be_txn_req *txn_req = NULL;
int index;
struct nb_cfg_change *cfg_chg;
- batch = mgmt_be_batch_create(txn, batch_id);
- if (!batch) {
- MGMTD_BE_CLIENT_ERR("Batch create failed!");
- return -1;
- }
+ batch = mgmt_be_batch_create(txn);
+ assert(batch);
txn_req = &batch->txn_req;
txn_req->event = MGMTD_BE_TXN_PROC_SETCFG;
- MGMTD_BE_CLIENT_DBG(
- "Created Set-Config request for batch 0x%llx, txn id 0x%llx, cfg-items:%d",
- (unsigned long long)batch_id, (unsigned long long)txn->txn_id,
- num_req);
+ debug_be_client("Created SETCFG request for txn-id: %" PRIu64
+ " cfg-items:%d",
+ txn->txn_id, num_req);
txn_req->req.set_cfg.num_cfg_changes = num_req;
for (index = 0; index < num_req; index++) {
cfg_chg = &txn_req->req.set_cfg.cfg_changes[index];
- if (cfg_req[index]->req_type
- == MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA)
+ /*
+ * Treat all operations as destroy or modify, because we don't
+ * need additional existence checks on the backend. Everything
+ * is already checked by mgmtd.
+ */
+ switch (cfg_req[index]->req_type) {
+ case MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA:
+ case MGMTD__CFG_DATA_REQ_TYPE__REMOVE_DATA:
cfg_chg->operation = NB_OP_DESTROY;
- else
- cfg_chg->operation = NB_OP_CREATE;
+ break;
+ case MGMTD__CFG_DATA_REQ_TYPE__SET_DATA:
+ case MGMTD__CFG_DATA_REQ_TYPE__CREATE_DATA:
+ case MGMTD__CFG_DATA_REQ_TYPE__REPLACE_DATA:
+ cfg_chg->operation = NB_OP_MODIFY;
+ break;
+ case MGMTD__CFG_DATA_REQ_TYPE__REQ_TYPE_NONE:
+ case _MGMTD__CFG_DATA_REQ_TYPE_IS_INT_SIZE:
+ default:
+ continue;
+ }
strlcpy(cfg_chg->xpath, cfg_req[index]->data->xpath,
sizeof(cfg_chg->xpath));
@@ -665,39 +645,34 @@ mgmt_be_update_setcfg_in_batch(struct mgmt_be_client_ctx *client_ctx,
return 0;
}
-static int
-mgmt_be_process_cfgdata_req(struct mgmt_be_client_ctx *client_ctx,
- uint64_t txn_id, uint64_t batch_id,
- Mgmtd__YangCfgDataReq * cfg_req[], int num_req,
- bool end_of_data)
+static int mgmt_be_process_cfgdata_req(struct mgmt_be_client *client_ctx,
+ uint64_t txn_id,
+ Mgmtd__YangCfgDataReq *cfg_req[],
+ int num_req, bool end_of_data)
{
struct mgmt_be_txn_ctx *txn;
- txn = mgmt_be_find_txn_by_id(client_ctx, txn_id);
- if (!txn) {
- MGMTD_BE_CLIENT_ERR(
- "Invalid txn-id 0x%llx provided from MGMTD server",
- (unsigned long long)txn_id);
- mgmt_be_send_cfgdata_create_reply(
- client_ctx, txn_id, batch_id, false,
- "Transaction context not created yet");
- } else {
- mgmt_be_update_setcfg_in_batch(client_ctx, txn, batch_id,
- cfg_req, num_req);
- }
+ txn = mgmt_be_find_txn_by_id(client_ctx, txn_id, true);
+ if (!txn)
+ goto failed;
+
+ mgmt_be_update_setcfg_in_batch(client_ctx, txn, cfg_req, num_req);
if (txn && end_of_data) {
- MGMTD_BE_CLIENT_DBG("Triggering CFG_PREPARE_REQ processing");
- mgmt_be_txn_cfg_prepare(txn);
+ debug_be_client("End of data; CFG_PREPARE_REQ processing");
+ if (mgmt_be_txn_cfg_prepare(txn))
+ goto failed;
}
return 0;
+failed:
+ msg_conn_disconnect(&client_ctx->client.conn, true);
+ return -1;
}
-static int mgmt_be_send_apply_reply(struct mgmt_be_client_ctx *client_ctx,
- uint64_t txn_id, uint64_t batch_ids[],
- size_t num_batch_ids, bool success,
- const char *error_if_any)
+static int mgmt_be_send_apply_reply(struct mgmt_be_client *client_ctx,
+ uint64_t txn_id, bool success,
+ const char *error_if_any)
{
Mgmtd__BeMessage be_msg;
Mgmtd__BeCfgDataApplyReply apply_reply;
@@ -705,8 +680,6 @@ static int mgmt_be_send_apply_reply(struct mgmt_be_client_ctx *client_ctx,
mgmtd__be_cfg_data_apply_reply__init(&apply_reply);
apply_reply.success = success;
apply_reply.txn_id = txn_id;
- apply_reply.batch_ids = (uint64_t *)batch_ids;
- apply_reply.n_batch_ids = num_batch_ids;
if (error_if_any)
apply_reply.error_if_any = (char *)error_if_any;
@@ -715,58 +688,41 @@ static int mgmt_be_send_apply_reply(struct mgmt_be_client_ctx *client_ctx,
be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY;
be_msg.cfg_apply_reply = &apply_reply;
- MGMTD_BE_CLIENT_DBG(
- "Sending CFG_APPLY_REPLY message to MGMTD for txn 0x%llx, %d batches [0x%llx - 0x%llx]",
- (unsigned long long)txn_id, (int)num_batch_ids,
- success && num_batch_ids ?
- (unsigned long long)batch_ids[0] : 0,
- success && num_batch_ids ?
- (unsigned long long)batch_ids[num_batch_ids - 1] : 0);
+ debug_be_client("Sending CFG_APPLY_REPLY txn-id %" PRIu64, txn_id);
return mgmt_be_client_send_msg(client_ctx, &be_msg);
}
static int mgmt_be_txn_proc_cfgapply(struct mgmt_be_txn_ctx *txn)
{
- struct mgmt_be_client_ctx *client_ctx;
+ struct mgmt_be_client *client_ctx;
struct timeval apply_nb_cfg_start;
struct timeval apply_nb_cfg_end;
unsigned long apply_nb_cfg_tm;
struct mgmt_be_batch_ctx *batch;
char err_buf[BUFSIZ];
- size_t num_processed;
- static uint64_t batch_ids[MGMTD_BE_MAX_BATCH_IDS_IN_REQ];
- bool debug_be = mgmt_debug_be_client;
- assert(txn && txn->client_ctx);
- client_ctx = txn->client_ctx;
+ assert(txn && txn->client);
+ client_ctx = txn->client;
assert(txn->nb_txn);
- num_processed = 0;
/*
* Now apply all the batches we have applied in one go.
*/
- if (debug_be)
- gettimeofday(&apply_nb_cfg_start, NULL);
+ gettimeofday(&apply_nb_cfg_start, NULL);
(void)nb_candidate_commit_apply(txn->nb_txn, true, &txn->nb_txn_id,
err_buf, sizeof(err_buf) - 1);
- if (debug_be) {
- gettimeofday(&apply_nb_cfg_end, NULL);
- apply_nb_cfg_tm =
- timeval_elapsed(apply_nb_cfg_end, apply_nb_cfg_start);
- client_ctx->avg_apply_nb_cfg_tm =
- ((client_ctx->avg_apply_nb_cfg_tm
- * client_ctx->num_apply_nb_cfg)
- + apply_nb_cfg_tm)
- / (client_ctx->num_apply_nb_cfg + 1);
- }
+ gettimeofday(&apply_nb_cfg_end, NULL);
+
+ apply_nb_cfg_tm = timeval_elapsed(apply_nb_cfg_end, apply_nb_cfg_start);
+ client_ctx->avg_apply_nb_cfg_tm = ((client_ctx->avg_apply_nb_cfg_tm *
+ client_ctx->num_apply_nb_cfg) +
+ apply_nb_cfg_tm) /
+ (client_ctx->num_apply_nb_cfg + 1);
client_ctx->num_apply_nb_cfg++;
txn->nb_txn = NULL;
- /*
- * Send back CFG_APPLY_REPLY for all batches applied.
- */
FOREACH_BE_APPLY_BATCH_IN_LIST (txn, batch) {
/*
* No need to delete the batch yet. Will be deleted during
@@ -775,96 +731,90 @@ static int mgmt_be_txn_proc_cfgapply(struct mgmt_be_txn_ctx *txn)
SET_FLAG(batch->flags, MGMTD_BE_TXN_FLAGS_CFG_APPLIED);
mgmt_be_batches_del(&txn->apply_cfgs, batch);
mgmt_be_batches_add_tail(&txn->cfg_batches, batch);
-
- batch_ids[num_processed] = batch->batch_id;
- num_processed++;
- if (num_processed == MGMTD_BE_MAX_BATCH_IDS_IN_REQ) {
- mgmt_be_send_apply_reply(client_ctx, txn->txn_id,
- batch_ids, num_processed,
- true, NULL);
- num_processed = 0;
- }
}
- mgmt_be_send_apply_reply(client_ctx, txn->txn_id, batch_ids,
- num_processed, true, NULL);
+ mgmt_be_send_apply_reply(client_ctx, txn->txn_id, true, NULL);
- if (debug_be)
- MGMTD_BE_CLIENT_DBG("Nb-apply-duration %lu (avg: %lu) uSec",
- apply_nb_cfg_tm,
- client_ctx->avg_apply_nb_cfg_tm);
+ debug_be_client("Nb-apply-duration %lu (avg: %lu) uSec",
+ apply_nb_cfg_tm, client_ctx->avg_apply_nb_cfg_tm);
return 0;
}
-static int
-mgmt_be_process_cfg_apply(struct mgmt_be_client_ctx *client_ctx,
- uint64_t txn_id)
+static int mgmt_be_process_cfg_apply(struct mgmt_be_client *client_ctx,
+ uint64_t txn_id)
{
struct mgmt_be_txn_ctx *txn;
- txn = mgmt_be_find_txn_by_id(client_ctx, txn_id);
- if (!txn) {
- mgmt_be_send_apply_reply(client_ctx, txn_id, NULL, 0, false,
- "Transaction not created yet!");
- return -1;
- }
+ txn = mgmt_be_find_txn_by_id(client_ctx, txn_id, true);
+ if (!txn)
+ goto failed;
- MGMTD_BE_CLIENT_DBG("Trigger CFG_APPLY_REQ processing");
- mgmt_be_txn_proc_cfgapply(txn);
+ debug_be_client("Trigger CFG_APPLY_REQ processing");
+ if (mgmt_be_txn_proc_cfgapply(txn))
+ goto failed;
return 0;
+failed:
+ msg_conn_disconnect(&client_ctx->client.conn, true);
+ return -1;
}
-static int
-mgmt_be_client_handle_msg(struct mgmt_be_client_ctx *client_ctx,
- Mgmtd__BeMessage *be_msg)
+
+static int mgmt_be_client_handle_msg(struct mgmt_be_client *client_ctx,
+ Mgmtd__BeMessage *be_msg)
{
/*
+ * On error we may have closed the connection so don't do anything with
+ * the client_ctx on return.
+ *
* protobuf-c adds a max size enum with an internal, and changing by
* version, name; cast to an int to avoid unhandled enum warnings
*/
switch ((int)be_msg->message_case) {
case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REPLY:
- MGMTD_BE_CLIENT_DBG("Subscribe Reply Msg from mgmt, status %u",
- be_msg->subscr_reply->success);
+ debug_be_client("Got SUBSCR_REPLY success %u",
+ be_msg->subscr_reply->success);
+
+ if (client_ctx->cbs.subscr_done)
+ (*client_ctx->cbs.subscr_done)(client_ctx,
+ client_ctx->user_data,
+ be_msg->subscr_reply
+ ->success);
break;
case MGMTD__BE_MESSAGE__MESSAGE_TXN_REQ:
+ debug_be_client("Got TXN_REQ %s txn-id: %" PRIu64,
+ be_msg->txn_req->create ? "Create" : "Delete",
+ be_msg->txn_req->txn_id);
mgmt_be_process_txn_req(client_ctx,
be_msg->txn_req->txn_id,
be_msg->txn_req->create);
break;
case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REQ:
+ debug_be_client("Got CFG_DATA_REQ txn-id: %" PRIu64
+ " end-of-data %u",
+ be_msg->cfg_data_req->txn_id,
+ be_msg->cfg_data_req->end_of_data);
mgmt_be_process_cfgdata_req(
client_ctx, be_msg->cfg_data_req->txn_id,
- be_msg->cfg_data_req->batch_id,
be_msg->cfg_data_req->data_req,
be_msg->cfg_data_req->n_data_req,
be_msg->cfg_data_req->end_of_data);
break;
case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REQ:
+ debug_be_client("Got CFG_APPLY_REQ txn-id: %" PRIu64,
+ be_msg->cfg_data_req->txn_id);
mgmt_be_process_cfg_apply(
client_ctx, (uint64_t)be_msg->cfg_apply_req->txn_id);
break;
- case MGMTD__BE_MESSAGE__MESSAGE_GET_REQ:
- case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ:
- case MGMTD__BE_MESSAGE__MESSAGE_CFG_CMD_REQ:
- case MGMTD__BE_MESSAGE__MESSAGE_SHOW_CMD_REQ:
- /*
- * TODO: Add handling code in future.
- */
- break;
/*
* NOTE: The following messages are always sent from Backend
* clients to MGMTd only and/or need not be handled here.
*/
- case MGMTD__BE_MESSAGE__MESSAGE_GET_REPLY:
+ case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ:
case MGMTD__BE_MESSAGE__MESSAGE_TXN_REPLY:
case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY:
case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY:
- case MGMTD__BE_MESSAGE__MESSAGE_CFG_CMD_REPLY:
- case MGMTD__BE_MESSAGE__MESSAGE_SHOW_CMD_REPLY:
- case MGMTD__BE_MESSAGE__MESSAGE_NOTIFY_DATA:
case MGMTD__BE_MESSAGE__MESSAGE__NOT_SET:
default:
/*
@@ -879,342 +829,381 @@ mgmt_be_client_handle_msg(struct mgmt_be_client_ctx *client_ctx,
return 0;
}
-static void mgmt_be_client_process_msg(void *user_ctx, uint8_t *data,
- size_t len)
-{
- struct mgmt_be_client_ctx *client_ctx = user_ctx;
- Mgmtd__BeMessage *be_msg;
+struct be_client_tree_data_batch_args {
+ struct mgmt_be_client *client;
+ uint64_t txn_id;
+ uint64_t req_id;
+ LYD_FORMAT result_type;
+};
- be_msg = mgmtd__be_message__unpack(NULL, len, data);
- if (!be_msg) {
- MGMTD_BE_CLIENT_DBG("Failed to decode %zu bytes from server",
- len);
- return;
+/*
+ * Process the get-tree request on our local oper state
+ */
+static enum nb_error be_client_send_tree_data_batch(const struct lyd_node *tree,
+ void *arg, enum nb_error ret)
+{
+ struct be_client_tree_data_batch_args *args = arg;
+ struct mgmt_be_client *client = args->client;
+ struct mgmt_msg_tree_data *tree_msg = NULL;
+ bool more = false;
+ uint8_t **darrp;
+ LY_ERR err;
+
+ if (ret == NB_YIELD) {
+ more = true;
+ ret = NB_OK;
}
- MGMTD_BE_CLIENT_DBG(
- "Decoded %zu bytes of message(msg: %u/%u) from server", len,
- be_msg->message_case, be_msg->message_case);
- (void)mgmt_be_client_handle_msg(client_ctx, be_msg);
- mgmtd__be_message__free_unpacked(be_msg, NULL);
+ if (ret != NB_OK)
+ goto done;
+
+ tree_msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_tree_data, 0,
+ MTYPE_MSG_NATIVE_TREE_DATA);
+ tree_msg->refer_id = args->txn_id;
+ tree_msg->req_id = args->req_id;
+ tree_msg->code = MGMT_MSG_CODE_TREE_DATA;
+ tree_msg->result_type = args->result_type;
+ tree_msg->more = more;
+
+ darrp = mgmt_msg_native_get_darrp(tree_msg);
+ err = yang_print_tree_append(darrp, tree, args->result_type,
+ (LYD_PRINT_SHRINK | LYD_PRINT_WD_EXPLICIT |
+ LYD_PRINT_WITHSIBLINGS));
+ if (err) {
+ ret = NB_ERR;
+ goto done;
+ }
+ (void)be_client_send_native_msg(client, tree_msg,
+ mgmt_msg_native_get_msg_len(tree_msg),
+ false);
+done:
+ mgmt_msg_native_free_msg(tree_msg);
+ if (ret)
+ be_client_send_error(client, args->txn_id, args->req_id, false,
+ -EINVAL,
+ "BE client %s txn-id %" PRIu64
+ " error fetching oper state %d",
+ client->name, args->txn_id, ret);
+ if (ret != NB_OK || !more)
+ XFREE(MTYPE_MGMTD_BE_GT_CB_ARGS, args);
+ return ret;
}
-static void mgmt_be_client_proc_msgbufs(struct event *thread)
+/*
+ * Process the get-tree request on our local oper state
+ */
+static void be_client_handle_get_tree(struct mgmt_be_client *client,
+ uint64_t txn_id, void *msgbuf,
+ size_t msg_len)
{
- struct mgmt_be_client_ctx *client_ctx = EVENT_ARG(thread);
+ struct mgmt_msg_get_tree *get_tree_msg = msgbuf;
+ struct be_client_tree_data_batch_args *args;
+
+ debug_be_client("Received get-tree request for client %s txn-id %" PRIu64
+ " req-id %" PRIu64,
+ client->name, txn_id, get_tree_msg->req_id);
+
+ /* NOTE: removed the translator, if put back merge with northbound_cli
+ * code
+ */
- if (mgmt_msg_procbufs(&client_ctx->mstate, mgmt_be_client_process_msg,
- client_ctx, mgmt_debug_be_client))
- mgmt_be_client_register_event(client_ctx, MGMTD_BE_PROC_MSG);
+ args = XMALLOC(MTYPE_MGMTD_BE_GT_CB_ARGS, sizeof(*args));
+ args->client = client;
+ args->txn_id = get_tree_msg->refer_id;
+ args->req_id = get_tree_msg->req_id;
+ args->result_type = get_tree_msg->result_type;
+ nb_oper_walk(get_tree_msg->xpath, NULL, 0, true, NULL, NULL,
+ be_client_send_tree_data_batch, args);
}
-static void mgmt_be_client_read(struct event *thread)
+/*
+ * Process the notification.
+ */
+static void be_client_handle_notify(struct mgmt_be_client *client, void *msgbuf,
+ size_t msg_len)
{
- struct mgmt_be_client_ctx *client_ctx = EVENT_ARG(thread);
- enum mgmt_msg_rsched rv;
+ struct mgmt_msg_notify_data *notif_msg = msgbuf;
+ struct nb_node *nb_node;
+ struct lyd_node *dnode;
+ const char *data;
+ const char *notif;
+ LY_ERR err;
+
+ debug_be_client("Received notification for client %s", client->name);
+
+ notif = mgmt_msg_native_xpath_data_decode(notif_msg, msg_len, data);
+ if (!notif || !data) {
+ log_err_be_client("Corrupt notify msg");
+ return;
+ }
- rv = mgmt_msg_read(&client_ctx->mstate, client_ctx->conn_fd,
- mgmt_debug_be_client);
- if (rv == MSR_DISCONNECT) {
- mgmt_be_server_disconnect(client_ctx, true);
+ nb_node = nb_node_find(notif);
+ if (!nb_node) {
+ log_err_be_client("No schema found for notification: %s", notif);
return;
}
- if (rv == MSR_SCHED_BOTH)
- mgmt_be_client_register_event(client_ctx, MGMTD_BE_PROC_MSG);
- mgmt_be_client_register_event(client_ctx, MGMTD_BE_CONN_READ);
-}
-static inline void
-mgmt_be_client_sched_msg_write(struct mgmt_be_client_ctx *client_ctx)
-{
- if (!CHECK_FLAG(client_ctx->flags, MGMTD_BE_CLIENT_FLAGS_WRITES_OFF))
- mgmt_be_client_register_event(client_ctx,
- MGMTD_BE_CONN_WRITE);
-}
+ if (!nb_node->cbs.notify) {
+ debug_be_client("No notification callback for: %s", notif);
+ return;
+ }
-static inline void
-mgmt_be_client_writes_on(struct mgmt_be_client_ctx *client_ctx)
-{
- MGMTD_BE_CLIENT_DBG("Resume writing msgs");
- UNSET_FLAG(client_ctx->flags, MGMTD_BE_CLIENT_FLAGS_WRITES_OFF);
- mgmt_be_client_sched_msg_write(client_ctx);
-}
+ err = yang_parse_notification(notif, notif_msg->result_type, data,
+ &dnode);
+ if (err) {
+ log_err_be_client("Can't parse notification data for: %s",
+ notif);
+ return;
+ }
-static inline void
-mgmt_be_client_writes_off(struct mgmt_be_client_ctx *client_ctx)
-{
- SET_FLAG(client_ctx->flags, MGMTD_BE_CLIENT_FLAGS_WRITES_OFF);
- MGMTD_BE_CLIENT_DBG("Paused writing msgs");
+ nb_callback_notify(nb_node, notif, dnode);
+
+ lyd_free_all(dnode);
}
-static int mgmt_be_client_send_msg(struct mgmt_be_client_ctx *client_ctx,
- Mgmtd__BeMessage *be_msg)
+/*
+ * Handle a native encoded message
+ *
+ * We don't create transactions with native messaging.
+ */
+static void be_client_handle_native_msg(struct mgmt_be_client *client,
+ struct mgmt_msg_header *msg,
+ size_t msg_len)
{
- if (client_ctx->conn_fd == -1) {
- MGMTD_BE_CLIENT_DBG("can't send message on closed connection");
- return -1;
- }
+ uint64_t txn_id = msg->refer_id;
- int rv = mgmt_msg_send_msg(
- &client_ctx->mstate, be_msg,
- mgmtd__be_message__get_packed_size(be_msg),
- (size_t(*)(void *, void *))mgmtd__be_message__pack,
- mgmt_debug_be_client);
- mgmt_be_client_sched_msg_write(client_ctx);
- return rv;
+ switch (msg->code) {
+ case MGMT_MSG_CODE_GET_TREE:
+ be_client_handle_get_tree(client, txn_id, msg, msg_len);
+ break;
+ case MGMT_MSG_CODE_NOTIFY:
+ be_client_handle_notify(client, msg, msg_len);
+ break;
+ default:
+ log_err_be_client("unknown native message txn-id %" PRIu64
+ " req-id %" PRIu64 " code %u to client %s",
+ txn_id, msg->req_id, msg->code, client->name);
+ be_client_send_error(client, msg->refer_id, msg->req_id, false,
+ -1,
+ "BE client %s recv msg unknown txn-id %" PRIu64,
+ client->name, txn_id);
+ break;
+ }
}
-static void mgmt_be_client_write(struct event *thread)
+static void mgmt_be_client_process_msg(uint8_t version, uint8_t *data,
+ size_t len, struct msg_conn *conn)
{
- struct mgmt_be_client_ctx *client_ctx = EVENT_ARG(thread);
- enum mgmt_msg_wsched rv;
-
- rv = mgmt_msg_write(&client_ctx->mstate, client_ctx->conn_fd,
- mgmt_debug_be_client);
- if (rv == MSW_SCHED_STREAM)
- mgmt_be_client_register_event(client_ctx, MGMTD_BE_CONN_WRITE);
- else if (rv == MSW_DISCONNECT)
- mgmt_be_server_disconnect(client_ctx, true);
- else if (rv == MSW_SCHED_WRITES_OFF) {
- mgmt_be_client_writes_off(client_ctx);
- mgmt_be_client_register_event(client_ctx,
- MGMTD_BE_CONN_WRITES_ON);
- } else
- assert(rv == MSW_SCHED_NONE);
-}
+ struct mgmt_be_client *client_ctx;
+ struct msg_client *client;
+ Mgmtd__BeMessage *be_msg;
-static void mgmt_be_client_resume_writes(struct event *thread)
-{
- struct mgmt_be_client_ctx *client_ctx;
+ client = container_of(conn, struct msg_client, conn);
+ client_ctx = container_of(client, struct mgmt_be_client, client);
- client_ctx = (struct mgmt_be_client_ctx *)EVENT_ARG(thread);
- assert(client_ctx && client_ctx->conn_fd != -1);
+ if (version == MGMT_MSG_VERSION_NATIVE) {
+ struct mgmt_msg_header *msg = (typeof(msg))data;
- mgmt_be_client_writes_on(client_ctx);
+ if (len >= sizeof(*msg))
+ be_client_handle_native_msg(client_ctx, msg, len);
+ else
+ log_err_be_client("native message to client %s too short %zu",
+ client_ctx->name, len);
+ return;
+ }
+
+ be_msg = mgmtd__be_message__unpack(NULL, len, data);
+ if (!be_msg) {
+ debug_be_client("Failed to decode %zu bytes from server", len);
+ return;
+ }
+ debug_be_client("Decoded %zu bytes of message(msg: %u/%u) from server",
+ len, be_msg->message_case, be_msg->message_case);
+ (void)mgmt_be_client_handle_msg(client_ctx, be_msg);
+ mgmtd__be_message__free_unpacked(be_msg, NULL);
}
-static int mgmt_be_send_subscr_req(struct mgmt_be_client_ctx *client_ctx,
- bool subscr_xpaths, uint16_t num_reg_xpaths,
- char **reg_xpaths)
+int mgmt_be_send_subscr_req(struct mgmt_be_client *client_ctx,
+ int n_config_xpaths, char **config_xpaths,
+ int n_oper_xpaths, char **oper_xpaths)
{
Mgmtd__BeMessage be_msg;
Mgmtd__BeSubscribeReq subscr_req;
mgmtd__be_subscribe_req__init(&subscr_req);
- subscr_req.client_name = client_ctx->client_params.name;
- subscr_req.n_xpath_reg = num_reg_xpaths;
- if (num_reg_xpaths)
- subscr_req.xpath_reg = reg_xpaths;
- else
- subscr_req.xpath_reg = NULL;
- subscr_req.subscribe_xpaths = subscr_xpaths;
+ subscr_req.client_name = client_ctx->name;
+ subscr_req.n_config_xpaths = n_config_xpaths;
+ subscr_req.config_xpaths = config_xpaths;
+ subscr_req.n_oper_xpaths = n_oper_xpaths;
+ subscr_req.oper_xpaths = oper_xpaths;
+
+ /* See if we should register for notifications */
+ subscr_req.n_notif_xpaths = client_ctx->cbs.nnotif_xpaths;
+ subscr_req.notif_xpaths = (char **)client_ctx->cbs.notif_xpaths;
mgmtd__be_message__init(&be_msg);
be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ;
be_msg.subscr_req = &subscr_req;
+ debug_be_client("Sending SUBSCR_REQ name: %s xpaths: config %zu oper: %zu notif: %zu",
+ subscr_req.client_name, subscr_req.n_config_xpaths,
+ subscr_req.n_oper_xpaths, subscr_req.n_notif_xpaths);
+
return mgmt_be_client_send_msg(client_ctx, &be_msg);
}
-static void mgmt_be_server_connect(struct mgmt_be_client_ctx *client_ctx)
+static int _notify_conenct_disconnect(struct msg_client *msg_client,
+ bool connected)
{
- const char *dbgtag = mgmt_debug_be_client ? "BE-client" : NULL;
-
- assert(client_ctx->conn_fd == -1);
- client_ctx->conn_fd = mgmt_msg_connect(
- MGMTD_BE_SERVER_PATH, MGMTD_SOCKET_BE_SEND_BUF_SIZE,
- MGMTD_SOCKET_BE_RECV_BUF_SIZE, dbgtag);
-
- /* Send SUBSCRIBE_REQ message */
- if (client_ctx->conn_fd == -1 ||
- mgmt_be_send_subscr_req(client_ctx, false, 0, NULL) != 0) {
- mgmt_be_server_disconnect(client_ctx, true);
- return;
+ struct mgmt_be_client *client =
+ container_of(msg_client, struct mgmt_be_client, client);
+ int ret;
+
+ if (connected) {
+ assert(msg_client->conn.fd != -1);
+ ret = mgmt_be_send_subscr_req(client, 0, NULL, 0, NULL);
+ if (ret)
+ return ret;
}
- /* Start reading from the socket */
- mgmt_be_client_register_event(client_ctx, MGMTD_BE_CONN_READ);
+ /* Notify BE client through registered callback (if any) */
+ if (client->cbs.client_connect_notify)
+ (void)(*client->cbs.client_connect_notify)(client,
+ client->user_data,
+ connected);
- /* Notify client through registered callback (if any) */
- if (client_ctx->client_params.client_connect_notify)
- (void)(*client_ctx->client_params.client_connect_notify)(
- (uintptr_t)client_ctx,
- client_ctx->client_params.user_data, true);
-}
+ /* Cleanup any in-progress TXN on disconnect */
+ if (!connected)
+ mgmt_be_cleanup_all_txns(client);
-static void mgmt_be_client_conn_timeout(struct event *thread)
-{
- mgmt_be_server_connect(EVENT_ARG(thread));
+ return 0;
}
-static void
-mgmt_be_client_register_event(struct mgmt_be_client_ctx *client_ctx,
- enum mgmt_be_event event)
+static int mgmt_be_client_notify_conenct(struct msg_client *client)
{
- struct timeval tv = {0};
-
- switch (event) {
- case MGMTD_BE_CONN_READ:
- event_add_read(client_ctx->tm, mgmt_be_client_read,
- client_ctx, client_ctx->conn_fd,
- &client_ctx->conn_read_ev);
- break;
- case MGMTD_BE_CONN_WRITE:
- event_add_write(client_ctx->tm, mgmt_be_client_write,
- client_ctx, client_ctx->conn_fd,
- &client_ctx->conn_write_ev);
- break;
- case MGMTD_BE_PROC_MSG:
- tv.tv_usec = MGMTD_BE_MSG_PROC_DELAY_USEC;
- event_add_timer_tv(client_ctx->tm, mgmt_be_client_proc_msgbufs,
- client_ctx, &tv, &client_ctx->msg_proc_ev);
- break;
- case MGMTD_BE_CONN_WRITES_ON:
- event_add_timer_msec(client_ctx->tm,
- mgmt_be_client_resume_writes, client_ctx,
- MGMTD_BE_MSG_WRITE_DELAY_MSEC,
- &client_ctx->conn_writes_on);
- break;
- case MGMTD_BE_SERVER:
- case MGMTD_BE_CONN_INIT:
- case MGMTD_BE_SCHED_CFG_PREPARE:
- case MGMTD_BE_RESCHED_CFG_PREPARE:
- case MGMTD_BE_SCHED_CFG_APPLY:
- case MGMTD_BE_RESCHED_CFG_APPLY:
- assert(!"mgmt_be_client_post_event() called incorrectly");
- break;
- }
+ return _notify_conenct_disconnect(client, true);
}
-static void
-mgmt_be_client_schedule_conn_retry(struct mgmt_be_client_ctx *client_ctx,
- unsigned long intvl_secs)
+static int mgmt_be_client_notify_disconenct(struct msg_conn *conn)
{
- MGMTD_BE_CLIENT_DBG(
- "Scheduling MGMTD Backend server connection retry after %lu seconds",
- intvl_secs);
- event_add_timer(client_ctx->tm, mgmt_be_client_conn_timeout,
- (void *)client_ctx, intvl_secs,
- &client_ctx->conn_retry_tmr);
-}
+ struct msg_client *client = container_of(conn, struct msg_client, conn);
-extern struct nb_config *running_config;
+ return _notify_conenct_disconnect(client, false);
+}
/*
- * Initialize library and try connecting with MGMTD.
+ * Debug Flags
*/
-uintptr_t mgmt_be_client_lib_init(struct mgmt_be_client_params *params,
- struct event_loop *master_thread)
-{
- assert(master_thread && params && strlen(params->name)
- && !mgmt_be_client_ctx.tm);
- mgmt_be_client_ctx.tm = master_thread;
+static void mgmt_debug_client_be_set(uint32_t flags, bool set)
+{
+ DEBUG_FLAGS_SET(&mgmt_dbg_be_client, flags, set);
- if (!running_config)
- assert(!"MGMTD Be Client lib_init() after frr_init() only!");
- mgmt_be_client_ctx.running_config = running_config;
- mgmt_be_client_ctx.candidate_config = nb_config_new(NULL);
+ if (!__be_client)
+ return;
- memcpy(&mgmt_be_client_ctx.client_params, params,
- sizeof(mgmt_be_client_ctx.client_params));
- if (!mgmt_be_client_ctx.client_params.conn_retry_intvl_sec)
- mgmt_be_client_ctx.client_params.conn_retry_intvl_sec =
- MGMTD_BE_DEFAULT_CONN_RETRY_INTVL_SEC;
+ __be_client->client.conn.debug = DEBUG_MODE_CHECK(&mgmt_dbg_be_client,
+ DEBUG_MODE_ALL);
+}
- mgmt_be_txns_init(&mgmt_be_client_ctx.txn_head);
- mgmt_msg_init(&mgmt_be_client_ctx.mstate, MGMTD_BE_MAX_NUM_MSG_PROC,
- MGMTD_BE_MAX_NUM_MSG_WRITE, MGMTD_BE_MSG_MAX_LEN,
- "BE-client");
+DEFPY(debug_mgmt_client_be, debug_mgmt_client_be_cmd,
+ "[no] debug mgmt client backend",
+ NO_STR DEBUG_STR MGMTD_STR "client\n"
+ "backend\n")
+{
+ mgmt_debug_client_be_set(DEBUG_NODE2MODE(vty->node), !no);
- /* Start trying to connect to MGMTD backend server immediately */
- mgmt_be_client_schedule_conn_retry(&mgmt_be_client_ctx, 1);
+ return CMD_SUCCESS;
+}
- MGMTD_BE_CLIENT_DBG("Initialized client '%s'", params->name);
+static int mgmt_debug_be_client_config_write(struct vty *vty)
+{
+ if (DEBUG_MODE_CHECK(&mgmt_dbg_be_client, DEBUG_MODE_CONF))
+ vty_out(vty, "debug mgmt client backend\n");
- return (uintptr_t)&mgmt_be_client_ctx;
+ return 1;
}
-/*
- * Subscribe with MGMTD for one or more YANG subtree(s).
- */
-enum mgmt_result mgmt_be_subscribe_yang_data(uintptr_t lib_hndl,
- char *reg_yang_xpaths[],
- int num_reg_xpaths)
+void mgmt_debug_be_client_show_debug(struct vty *vty)
{
- struct mgmt_be_client_ctx *client_ctx;
-
- client_ctx = (struct mgmt_be_client_ctx *)lib_hndl;
- if (!client_ctx)
- return MGMTD_INVALID_PARAM;
+ if (debug_check_be_client())
+ vty_out(vty, "debug mgmt client backend\n");
+}
- if (mgmt_be_send_subscr_req(client_ctx, true, num_reg_xpaths,
- reg_yang_xpaths)
- != 0)
- return MGMTD_INTERNAL_ERROR;
+static struct debug_callbacks mgmt_dbg_be_client_cbs = {
+ .debug_set_all = mgmt_debug_client_be_set
+};
- return MGMTD_SUCCESS;
-}
+static struct cmd_node mgmt_dbg_node = {
+ .name = "debug mgmt client backend",
+ .node = MGMT_BE_DEBUG_NODE,
+ .prompt = "",
+ .config_write = mgmt_debug_be_client_config_write,
+};
-/*
- * Unsubscribe with MGMTD for one or more YANG subtree(s).
- */
-enum mgmt_result mgmt_be_unsubscribe_yang_data(uintptr_t lib_hndl,
- char *reg_yang_xpaths[],
- int num_reg_xpaths)
+struct mgmt_be_client *mgmt_be_client_create(const char *client_name,
+ struct mgmt_be_client_cbs *cbs,
+ uintptr_t user_data,
+ struct event_loop *event_loop)
{
- struct mgmt_be_client_ctx *client_ctx;
+ struct mgmt_be_client *client;
+ char server_path[MAXPATHLEN];
- client_ctx = (struct mgmt_be_client_ctx *)lib_hndl;
- if (!client_ctx)
- return MGMTD_INVALID_PARAM;
+ if (__be_client)
+ return NULL;
+ client = XCALLOC(MTYPE_MGMTD_BE_CLIENT, sizeof(*client));
+ __be_client = client;
- if (mgmt_be_send_subscr_req(client_ctx, false, num_reg_xpaths,
- reg_yang_xpaths)
- < 0)
- return MGMTD_INTERNAL_ERROR;
+ /* Only call after frr_init() */
+ assert(running_config);
- return MGMTD_SUCCESS;
-}
+ client->name = XSTRDUP(MTYPE_MGMTD_BE_CLIENT_NAME, client_name);
+ client->running_config = running_config;
+ client->candidate_config = vty_shared_candidate_config;
+ if (cbs)
+ client->cbs = *cbs;
+ mgmt_be_txns_init(&client->txn_head);
-/*
- * Send one or more YANG notifications to MGMTD daemon.
- */
-enum mgmt_result mgmt_be_send_yang_notify(uintptr_t lib_hndl,
- Mgmtd__YangData * data_elems[],
- int num_elems)
-{
- struct mgmt_be_client_ctx *client_ctx;
+ snprintf(server_path, sizeof(server_path), MGMTD_BE_SOCK_NAME);
+
+ msg_client_init(&client->client, event_loop, server_path,
+ mgmt_be_client_notify_conenct,
+ mgmt_be_client_notify_disconenct,
+ mgmt_be_client_process_msg, MGMTD_BE_MAX_NUM_MSG_PROC,
+ MGMTD_BE_MAX_NUM_MSG_WRITE, MGMTD_BE_MAX_MSG_LEN, false,
+ "BE-client", debug_check_be_client());
+
+ /* Hook to receive notifications */
+ hook_register_arg(nb_notification_tree_send, mgmt_be_send_notification,
+ client);
- client_ctx = (struct mgmt_be_client_ctx *)lib_hndl;
- if (!client_ctx)
- return MGMTD_INVALID_PARAM;
+ debug_be_client("Initialized client '%s'", client_name);
- return MGMTD_SUCCESS;
+ return client;
}
-/*
- * Destroy library and cleanup everything.
- */
-void mgmt_be_client_lib_destroy(uintptr_t lib_hndl)
+
+void mgmt_be_client_lib_vty_init(void)
{
- struct mgmt_be_client_ctx *client_ctx;
+ debug_init(&mgmt_dbg_be_client_cbs);
+ install_node(&mgmt_dbg_node);
+ install_element(ENABLE_NODE, &debug_mgmt_client_be_cmd);
+ install_element(CONFIG_NODE, &debug_mgmt_client_be_cmd);
+}
- client_ctx = (struct mgmt_be_client_ctx *)lib_hndl;
- assert(client_ctx);
+void mgmt_be_client_destroy(struct mgmt_be_client *client)
+{
+ assert(client == __be_client);
- MGMTD_BE_CLIENT_DBG("Destroying MGMTD Backend Client '%s'",
- client_ctx->client_params.name);
+ debug_be_client("Destroying MGMTD Backend Client '%s'", client->name);
- mgmt_be_server_disconnect(client_ctx, false);
+ nb_oper_cancel_all_walks();
+ msg_client_cleanup(&client->client);
+ mgmt_be_cleanup_all_txns(client);
+ mgmt_be_txns_fini(&client->txn_head);
- mgmt_msg_destroy(&client_ctx->mstate);
+ XFREE(MTYPE_MGMTD_BE_CLIENT_NAME, client->name);
+ XFREE(MTYPE_MGMTD_BE_CLIENT, client);
- EVENT_OFF(client_ctx->conn_retry_tmr);
- EVENT_OFF(client_ctx->conn_read_ev);
- EVENT_OFF(client_ctx->conn_write_ev);
- EVENT_OFF(client_ctx->conn_writes_on);
- EVENT_OFF(client_ctx->msg_proc_ev);
- mgmt_be_cleanup_all_txns(client_ctx);
- mgmt_be_txns_fini(&client_ctx->txn_head);
+ __be_client = NULL;
}
diff --git a/lib/mgmt_be_client.h b/lib/mgmt_be_client.h
index db427457a4..cd8b237526 100644
--- a/lib/mgmt_be_client.h
+++ b/lib/mgmt_be_client.h
@@ -14,52 +14,18 @@ extern "C" {
#include "northbound.h"
#include "mgmt_pb.h"
-#include "mgmtd/mgmt_defines.h"
-
-/***************************************************************
- * Client IDs
- ***************************************************************/
-
-/*
- * Add enum value for each supported component, wrap with
- * #ifdef HAVE_COMPONENT
- */
-enum mgmt_be_client_id {
- MGMTD_BE_CLIENT_ID_MIN = 0,
- MGMTD_BE_CLIENT_ID_INIT = -1,
-#ifdef HAVE_STATICD
- MGMTD_BE_CLIENT_ID_STATICD,
-#endif
- MGMTD_BE_CLIENT_ID_MAX
-};
-
-#define FOREACH_MGMTD_BE_CLIENT_ID(id) \
- for ((id) = MGMTD_BE_CLIENT_ID_MIN; \
- (id) < MGMTD_BE_CLIENT_ID_MAX; (id)++)
+#include "mgmt_defines.h"
/***************************************************************
* Constants
***************************************************************/
-#define MGMTD_BE_CLIENT_ERROR_STRING_MAX_LEN 32
-
-#define MGMTD_BE_DEFAULT_CONN_RETRY_INTVL_SEC 5
-
-#define MGMTD_BE_MSG_PROC_DELAY_USEC 10
-#define MGMTD_BE_MAX_NUM_MSG_PROC 500
-
-#define MGMTD_BE_MSG_WRITE_DELAY_MSEC 1
+#define MGMTD_BE_MAX_NUM_MSG_PROC 500
#define MGMTD_BE_MAX_NUM_MSG_WRITE 1000
+#define MGMTD_BE_MAX_MSG_LEN (64 * 1024)
-#define GMGD_BE_MAX_NUM_REQ_ITEMS 64
-
-#define MGMTD_BE_MSG_MAX_LEN 16384
-
-#define MGMTD_SOCKET_BE_SEND_BUF_SIZE 65535
-#define MGMTD_SOCKET_BE_RECV_BUF_SIZE MGMTD_SOCKET_BE_SEND_BUF_SIZE
-
-#define MGMTD_MAX_CFG_CHANGES_IN_BATCH \
- ((10 * MGMTD_BE_MSG_MAX_LEN) / \
+#define MGMTD_MAX_CFG_CHANGES_IN_BATCH \
+ ((10 * MGMTD_BE_MAX_MSG_LEN) / \
(MGMTD_MAX_XPATH_LEN + MGMTD_MAX_YANG_VALUE_LEN))
/*
@@ -68,11 +34,11 @@ enum mgmt_be_client_id {
* that gets added to sent message
*/
#define MGMTD_BE_CFGDATA_PACKING_EFFICIENCY 0.8
-#define MGMTD_BE_CFGDATA_MAX_MSG_LEN \
- (MGMTD_BE_MSG_MAX_LEN * MGMTD_BE_CFGDATA_PACKING_EFFICIENCY)
+#define MGMTD_BE_CFGDATA_MAX_MSG_LEN \
+ (MGMTD_BE_MAX_MSG_LEN * MGMTD_BE_CFGDATA_PACKING_EFFICIENCY)
-#define MGMTD_BE_MAX_BATCH_IDS_IN_REQ \
- (MGMTD_BE_MSG_MAX_LEN - 128) / sizeof(uint64_t)
+#define MGMTD_BE_MAX_BATCH_IDS_IN_REQ \
+ (MGMTD_BE_MAX_MSG_LEN - 128) / sizeof(uint64_t)
#define MGMTD_BE_CONTAINER_NODE_VAL "<<container>>"
@@ -82,176 +48,105 @@ enum mgmt_be_client_id {
#define MGMTD_BE_MAX_CLIENTS_PER_XPATH_REG 32
+struct mgmt_be_client;
+
struct mgmt_be_client_txn_ctx {
uintptr_t *user_ctx;
};
-/*
- * All the client-specific information this library needs to
- * initialize itself, setup connection with MGMTD BackEnd interface
- * and carry on all required procedures appropriately.
+/**
+ * Backend client callbacks.
+ *
+ * Callbacks:
+ * client_connect_notify: called when connection is made/lost to mgmtd.
+ * txn_notify: called when a txn has been created
+ * notify_cbs: callbacks for notifications.
+ * nnotify_cbs: number of notification callbacks.
*
- * BackEnd clients need to initialise a instance of this structure
- * with appropriate data and pass it while calling the API
- * to initialize the library (See mgmt_be_client_lib_init for
- * more details).
*/
-struct mgmt_be_client_params {
- char name[MGMTD_CLIENT_NAME_MAX_LEN];
- uintptr_t user_data;
- unsigned long conn_retry_intvl_sec;
-
- void (*client_connect_notify)(uintptr_t lib_hndl,
- uintptr_t usr_data,
- bool connected);
-
- void (*client_subscribe_notify)(
- uintptr_t lib_hndl, uintptr_t usr_data,
- struct nb_yang_xpath **xpath,
- enum mgmt_result subscribe_result[], int num_paths);
-
- void (*txn_notify)(
- uintptr_t lib_hndl, uintptr_t usr_data,
- struct mgmt_be_client_txn_ctx *txn_ctx, bool destroyed);
-
- enum mgmt_result (*data_validate)(
- uintptr_t lib_hndl, uintptr_t usr_data,
- struct mgmt_be_client_txn_ctx *txn_ctx,
- struct nb_yang_xpath *xpath, struct nb_yang_value *data,
- bool delete, char *error_if_any);
-
- enum mgmt_result (*data_apply)(
- uintptr_t lib_hndl, uintptr_t usr_data,
- struct mgmt_be_client_txn_ctx *txn_ctx,
- struct nb_yang_xpath *xpath, struct nb_yang_value *data,
- bool delete);
-
- enum mgmt_result (*get_data_elem)(
- uintptr_t lib_hndl, uintptr_t usr_data,
- struct mgmt_be_client_txn_ctx *txn_ctx,
- struct nb_yang_xpath *xpath, struct nb_yang_xpath_elem *elem);
-
- enum mgmt_result (*get_data)(
- uintptr_t lib_hndl, uintptr_t usr_data,
- struct mgmt_be_client_txn_ctx *txn_ctx,
- struct nb_yang_xpath *xpath, bool keys_only,
- struct nb_yang_xpath_elem **elems, int *num_elems,
- int *next_key);
-
- enum mgmt_result (*get_next_data)(
- uintptr_t lib_hndl, uintptr_t usr_data,
- struct mgmt_be_client_txn_ctx *txn_ctx,
- struct nb_yang_xpath *xpath, bool keys_only,
- struct nb_yang_xpath_elem **elems, int *num_elems);
+struct mgmt_be_client_cbs {
+ void (*client_connect_notify)(struct mgmt_be_client *client,
+ uintptr_t usr_data, bool connected);
+ void (*subscr_done)(struct mgmt_be_client *client, uintptr_t usr_data,
+ bool success);
+ void (*txn_notify)(struct mgmt_be_client *client, uintptr_t usr_data,
+ struct mgmt_be_client_txn_ctx *txn_ctx,
+ bool destroyed);
+
+ const char **notif_xpaths;
+ uint nnotif_xpaths;
};
/***************************************************************
* Global data exported
***************************************************************/
-extern const char *mgmt_be_client_names[MGMTD_BE_CLIENT_ID_MAX + 1];
-
-static inline const char *mgmt_be_client_id2name(enum mgmt_be_client_id id)
-{
- if (id > MGMTD_BE_CLIENT_ID_MAX)
- id = MGMTD_BE_CLIENT_ID_MAX;
- return mgmt_be_client_names[id];
-}
-
-static inline enum mgmt_be_client_id
-mgmt_be_client_name2id(const char *name)
-{
- enum mgmt_be_client_id id;
-
- FOREACH_MGMTD_BE_CLIENT_ID (id) {
- if (!strncmp(mgmt_be_client_names[id], name,
- MGMTD_CLIENT_NAME_MAX_LEN))
- return id;
- }
-
- return MGMTD_BE_CLIENT_ID_MAX;
-}
+extern struct debug mgmt_dbg_be_client;
/***************************************************************
* API prototypes
***************************************************************/
-/*
- * Initialize library and try connecting with MGMTD.
- *
- * params
- * Backend client parameters.
+#define debug_be_client(fmt, ...) \
+ DEBUGD(&mgmt_dbg_be_client, "BE-CLIENT: %s: " fmt, __func__, \
+ ##__VA_ARGS__)
+#define log_err_be_client(fmt, ...) \
+ zlog_err("BE-CLIENT: %s: ERROR: " fmt, __func__, ##__VA_ARGS__)
+#define debug_check_be_client() \
+ DEBUG_MODE_CHECK(&mgmt_dbg_be_client, DEBUG_MODE_ALL)
+
+/**
+ * Create backend client and connect to MGMTD.
*
- * master_thread
- * Thread master.
+ * Args:
+ * client_name: the name of the client
+ * cbs: callbacks for various events.
+ * event_loop: the main event loop.
*
* Returns:
- * Backend client lib handler (nothing but address of mgmt_be_client_ctx)
+ * Backend client object.
*/
-extern uintptr_t mgmt_be_client_lib_init(struct mgmt_be_client_params *params,
- struct event_loop *master_thread);
+extern struct mgmt_be_client *
+mgmt_be_client_create(const char *name, struct mgmt_be_client_cbs *cbs,
+ uintptr_t user_data, struct event_loop *event_loop);
/*
- * Subscribe with MGMTD for one or more YANG subtree(s).
- *
- * lib_hndl
- * Client library handler.
+ * Initialize library vty (adds debug support).
*
- * reg_yang_xpaths
- * Yang xpath(s) that needs to be subscribed to.
- *
- * num_xpaths
- * Number of xpaths
- *
- * Returns:
- * MGMTD_SUCCESS on success, MGMTD_* otherwise.
+ * This call should be added to your component when enabling other vty code to
+ * enable mgmtd client debugs. When adding, one needs to also add a their
+ * component in `xref2vtysh.py` as well.
*/
-extern enum mgmt_result mgmt_be_subscribe_yang_data(uintptr_t lib_hndl,
- char **reg_yang_xpaths,
- int num_xpaths);
+extern void mgmt_be_client_lib_vty_init(void);
/*
- * Send one or more YANG notifications to MGMTD daemon.
- *
- * lib_hndl
- * Client library handler.
- *
- * data_elems
- * Yang data elements from data tree.
- *
- * num_elems
- * Number of data elements.
- *
- * Returns:
- * MGMTD_SUCCESS on success, MGMTD_* otherwise.
+ * Print enabled debugging commands.
*/
-extern enum mgmt_result
-mgmt_be_send_yang_notify(uintptr_t lib_hndl, Mgmtd__YangData **data_elems,
- int num_elems);
+extern void mgmt_debug_be_client_show_debug(struct vty *vty);
/*
- * Un-subscribe with MGMTD for one or more YANG subtree(s).
+ * [Un]-subscribe with MGMTD for one or more YANG subtree(s).
*
- * lib_hndl
- * Client library handler.
+ * client
+ * The client object.
*
* reg_yang_xpaths
- * Yang xpath(s) that needs to be un-subscribed from.
+ * Yang xpath(s) that needs to be subscribed to
*
- * num_reg_xpaths
- * Number of subscribed xpaths
+ * num_xpaths
+ * Number of xpaths
*
* Returns:
* MGMTD_SUCCESS on success, MGMTD_* otherwise.
*/
-enum mgmt_result mgmt_be_unsubscribe_yang_data(uintptr_t lib_hndl,
- char **reg_yang_xpaths,
- int num_reg_xpaths);
+extern int mgmt_be_send_subscr_req(struct mgmt_be_client *client_ctx,
+ int n_config_xpaths, char **config_xpaths,
+ int n_oper_xpaths, char **oper_xpaths);
/*
- * Destroy library and cleanup everything.
+ * Destroy backend client and cleanup everything.
*/
-extern void mgmt_be_client_lib_destroy(uintptr_t lib_hndl);
+extern void mgmt_be_client_destroy(struct mgmt_be_client *client);
#ifdef __cplusplus
}
diff --git a/lib/mgmt_defines.h b/lib/mgmt_defines.h
new file mode 100644
index 0000000000..b02341ea4e
--- /dev/null
+++ b/lib/mgmt_defines.h
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MGMTD public defines.
+ *
+ * Copyright (C) 2021 Vmware, Inc.
+ * Pushpasis Sarkar <spushpasis@vmware.com>
+ */
+
+#ifndef _FRR_MGMTD_DEFINES_H
+#define _FRR_MGMTD_DEFINES_H
+
+#include "yang.h"
+
+#define MGMTD_FE_SOCK_NAME "%s/mgmtd_fe.sock", frr_runstatedir
+#define MGMTD_BE_SOCK_NAME "%s/mgmtd_be.sock", frr_runstatedir
+
+#define MGMTD_CLIENT_NAME_MAX_LEN 32
+
+#define MGMTD_MAX_XPATH_LEN XPATH_MAXLEN
+
+#define MGMTD_MAX_YANG_VALUE_LEN YANG_VALUE_MAXLEN
+
+#define MGMTD_MAX_NUM_XPATH_REG 128
+
+#define MGMTD_MAX_NUM_DATA_REQ_IN_BATCH 32
+#define MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH 8
+
+enum mgmt_result {
+ MGMTD_SUCCESS = 0,
+ MGMTD_INVALID_PARAM,
+ MGMTD_INTERNAL_ERROR,
+ MGMTD_NO_CFG_CHANGES,
+ MGMTD_DS_LOCK_FAILED,
+ MGMTD_DS_UNLOCK_FAILED,
+ MGMTD_UNKNOWN_FAILURE
+};
+
+#endif /* _FRR_MGMTD_DEFINES_H */
diff --git a/lib/mgmt_fe_client.c b/lib/mgmt_fe_client.c
index 7cb9aa3def..a107582bea 100644
--- a/lib/mgmt_fe_client.c
+++ b/lib/mgmt_fe_client.c
@@ -6,38 +6,26 @@
*/
#include <zebra.h>
+#include "compiler.h"
+#include "debug.h"
#include "memory.h"
#include "libfrr.h"
#include "mgmt_fe_client.h"
#include "mgmt_msg.h"
+#include "mgmt_msg_native.h"
#include "mgmt_pb.h"
#include "network.h"
#include "stream.h"
#include "sockopt.h"
-#ifdef REDIRECT_DEBUG_TO_STDERR
-#define MGMTD_FE_CLIENT_DBG(fmt, ...) \
- fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
-#define MGMTD_FE_CLIENT_ERR(fmt, ...) \
- fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
-#else /* REDIRECT_DEBUG_TO_STDERR */
-#define MGMTD_FE_CLIENT_DBG(fmt, ...) \
- do { \
- if (mgmt_debug_fe_client) \
- zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__); \
- } while (0)
-#define MGMTD_FE_CLIENT_ERR(fmt, ...) \
- zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
-#endif /* REDIRECT_DEBUG_TO_STDERR */
-
-struct mgmt_fe_client_ctx;
+#include "lib/mgmt_fe_client_clippy.c"
PREDECL_LIST(mgmt_sessions);
struct mgmt_fe_client_session {
- uint64_t client_id;
- uint64_t session_id;
- struct mgmt_fe_client_ctx *client_ctx;
+ uint64_t client_id; /* FE client identifies itself with this ID */
+ uint64_t session_id; /* FE adapter identified session with this ID */
+ struct mgmt_fe_client *client;
uintptr_t user_ctx;
struct mgmt_sessions_item list_linkage;
@@ -45,188 +33,112 @@ struct mgmt_fe_client_session {
DECLARE_LIST(mgmt_sessions, struct mgmt_fe_client_session, list_linkage);
-DEFINE_MTYPE_STATIC(LIB, MGMTD_FE_SESSION, "MGMTD Frontend session");
+DEFINE_MTYPE_STATIC(LIB, MGMTD_FE_CLIENT, "frontend client");
+DEFINE_MTYPE_STATIC(LIB, MGMTD_FE_CLIENT_NAME, "frontend client name");
+DEFINE_MTYPE_STATIC(LIB, MGMTD_FE_SESSION, "frontend session");
-struct mgmt_fe_client_ctx {
- int conn_fd;
- struct event_loop *tm;
- struct event *conn_retry_tmr;
- struct event *conn_read_ev;
- struct event *conn_write_ev;
- struct event *conn_writes_on;
- struct event *msg_proc_ev;
- uint32_t flags;
-
- struct mgmt_msg_state mstate;
-
- struct mgmt_fe_client_params client_params;
-
- struct mgmt_sessions_head client_sessions;
+struct mgmt_fe_client {
+ struct msg_client client;
+ char *name;
+ struct mgmt_fe_client_cbs cbs;
+ uintptr_t user_data;
+ struct mgmt_sessions_head sessions;
};
-#define MGMTD_FE_CLIENT_FLAGS_WRITES_OFF (1U << 0)
-
-#define FOREACH_SESSION_IN_LIST(client_ctx, session) \
- frr_each_safe (mgmt_sessions, &(client_ctx)->client_sessions, (session))
+#define FOREACH_SESSION_IN_LIST(client, session) \
+ frr_each_safe (mgmt_sessions, &(client)->sessions, (session))
-static bool mgmt_debug_fe_client;
-
-static struct mgmt_fe_client_ctx mgmt_fe_client_ctx = {
- .conn_fd = -1,
+struct debug mgmt_dbg_fe_client = {
+ .desc = "Management frontend client operations"
};
-/* Forward declarations */
-static void
-mgmt_fe_client_register_event(struct mgmt_fe_client_ctx *client_ctx,
- enum mgmt_fe_event event);
-static void mgmt_fe_client_schedule_conn_retry(
- struct mgmt_fe_client_ctx *client_ctx, unsigned long intvl_secs);
+/* NOTE: only one client per proc for now. */
+static struct mgmt_fe_client *__fe_client;
+
+static inline const char *dsid2name(Mgmtd__DatastoreId id)
+{
+ switch ((int)id) {
+ case MGMTD_DS_NONE:
+ return "none";
+ case MGMTD_DS_RUNNING:
+ return "running";
+ case MGMTD_DS_CANDIDATE:
+ return "candidate";
+ case MGMTD_DS_OPERATIONAL:
+ return "operational";
+ default:
+ return "unknown-datastore-id";
+ }
+}
static struct mgmt_fe_client_session *
-mgmt_fe_find_session_by_client_id(struct mgmt_fe_client_ctx *client_ctx,
- uint64_t client_id)
+mgmt_fe_find_session_by_client_id(struct mgmt_fe_client *client,
+ uint64_t client_id)
{
struct mgmt_fe_client_session *session;
- FOREACH_SESSION_IN_LIST (client_ctx, session) {
+ FOREACH_SESSION_IN_LIST (client, session) {
if (session->client_id == client_id) {
- MGMTD_FE_CLIENT_DBG(
- "Found session %p for client-id %llu.", session,
- (unsigned long long)client_id);
+ debug_fe_client("Found session-id %" PRIu64
+ " using client-id %" PRIu64,
+ session->session_id, client_id);
return session;
}
}
-
+ debug_fe_client("Session not found using client-id %" PRIu64, client_id);
return NULL;
}
static struct mgmt_fe_client_session *
-mgmt_fe_find_session_by_session_id(struct mgmt_fe_client_ctx *client_ctx,
- uint64_t session_id)
+mgmt_fe_find_session_by_session_id(struct mgmt_fe_client *client,
+ uint64_t session_id)
{
struct mgmt_fe_client_session *session;
- FOREACH_SESSION_IN_LIST (client_ctx, session) {
+ FOREACH_SESSION_IN_LIST (client, session) {
if (session->session_id == session_id) {
- MGMTD_FE_CLIENT_DBG(
- "Found session %p for session-id %llu.",
- session, (unsigned long long)session_id);
+ debug_fe_client("Found session of client-id %" PRIu64
+ " using session-id %" PRIu64,
+ session->client_id, session_id);
return session;
}
}
-
+ debug_fe_client("Session not found using session-id %" PRIu64,
+ session_id);
return NULL;
}
-static void
-mgmt_fe_server_disconnect(struct mgmt_fe_client_ctx *client_ctx,
- bool reconnect)
+static int mgmt_fe_client_send_msg(struct mgmt_fe_client *client,
+ Mgmtd__FeMessage *fe_msg,
+ bool short_circuit_ok)
{
- if (client_ctx->conn_fd != -1) {
- close(client_ctx->conn_fd);
- client_ctx->conn_fd = -1;
- }
-
- if (reconnect)
- mgmt_fe_client_schedule_conn_retry(
- client_ctx,
- client_ctx->client_params.conn_retry_intvl_sec);
-}
-
-static inline void
-mgmt_fe_client_sched_msg_write(struct mgmt_fe_client_ctx *client_ctx)
-{
- if (!CHECK_FLAG(client_ctx->flags, MGMTD_FE_CLIENT_FLAGS_WRITES_OFF))
- mgmt_fe_client_register_event(client_ctx,
- MGMTD_FE_CONN_WRITE);
-}
-
-static inline void
-mgmt_fe_client_writes_on(struct mgmt_fe_client_ctx *client_ctx)
-{
- MGMTD_FE_CLIENT_DBG("Resume writing msgs");
- UNSET_FLAG(client_ctx->flags, MGMTD_FE_CLIENT_FLAGS_WRITES_OFF);
- mgmt_fe_client_sched_msg_write(client_ctx);
-}
-
-static inline void
-mgmt_fe_client_writes_off(struct mgmt_fe_client_ctx *client_ctx)
-{
- SET_FLAG(client_ctx->flags, MGMTD_FE_CLIENT_FLAGS_WRITES_OFF);
- MGMTD_FE_CLIENT_DBG("Paused writing msgs");
-}
-
-static int mgmt_fe_client_send_msg(struct mgmt_fe_client_ctx *client_ctx,
- Mgmtd__FeMessage *fe_msg)
-{
- /* users current expect this to fail here */
- if (client_ctx->conn_fd == -1) {
- MGMTD_FE_CLIENT_DBG("can't send message on closed connection");
- return -1;
- }
-
- int rv = mgmt_msg_send_msg(
- &client_ctx->mstate, fe_msg,
+ return msg_conn_send_msg(
+ &client->client.conn, MGMT_MSG_VERSION_PROTOBUF, fe_msg,
mgmtd__fe_message__get_packed_size(fe_msg),
(size_t(*)(void *, void *))mgmtd__fe_message__pack,
- mgmt_debug_fe_client);
- mgmt_fe_client_sched_msg_write(client_ctx);
- return rv;
+ short_circuit_ok);
}
-static void mgmt_fe_client_write(struct event *thread)
-{
- struct mgmt_fe_client_ctx *client_ctx;
- enum mgmt_msg_wsched rv;
-
- client_ctx = (struct mgmt_fe_client_ctx *)EVENT_ARG(thread);
- rv = mgmt_msg_write(&client_ctx->mstate, client_ctx->conn_fd,
- mgmt_debug_fe_client);
- if (rv == MSW_SCHED_STREAM)
- mgmt_fe_client_register_event(client_ctx, MGMTD_FE_CONN_WRITE);
- else if (rv == MSW_DISCONNECT)
- mgmt_fe_server_disconnect(client_ctx, true);
- else if (rv == MSW_SCHED_WRITES_OFF) {
- mgmt_fe_client_writes_off(client_ctx);
- mgmt_fe_client_register_event(client_ctx,
- MGMTD_FE_CONN_WRITES_ON);
- } else
- assert(rv == MSW_SCHED_NONE);
-}
-
-static void mgmt_fe_client_resume_writes(struct event *thread)
-{
- struct mgmt_fe_client_ctx *client_ctx;
-
- client_ctx = (struct mgmt_fe_client_ctx *)EVENT_ARG(thread);
- assert(client_ctx && client_ctx->conn_fd != -1);
-
- mgmt_fe_client_writes_on(client_ctx);
-}
-
-static int
-mgmt_fe_send_register_req(struct mgmt_fe_client_ctx *client_ctx)
+static int mgmt_fe_send_register_req(struct mgmt_fe_client *client)
{
Mgmtd__FeMessage fe_msg;
Mgmtd__FeRegisterReq rgstr_req;
mgmtd__fe_register_req__init(&rgstr_req);
- rgstr_req.client_name = client_ctx->client_params.name;
+ rgstr_req.client_name = client->name;
mgmtd__fe_message__init(&fe_msg);
fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_REGISTER_REQ;
fe_msg.register_req = &rgstr_req;
- MGMTD_FE_CLIENT_DBG(
- "Sending REGISTER_REQ message to MGMTD Frontend server");
+ debug_fe_client("Sending REGISTER_REQ message to MGMTD Frontend server");
- return mgmt_fe_client_send_msg(client_ctx, &fe_msg);
+ return mgmt_fe_client_send_msg(client, &fe_msg, true);
}
-static int
-mgmt_fe_send_session_req(struct mgmt_fe_client_ctx *client_ctx,
- struct mgmt_fe_client_session *session,
- bool create)
+static int mgmt_fe_send_session_req(struct mgmt_fe_client *client,
+ struct mgmt_fe_client_session *session,
+ bool create)
{
Mgmtd__FeMessage fe_msg;
Mgmtd__FeSessionReq sess_req;
@@ -245,25 +157,22 @@ mgmt_fe_send_session_req(struct mgmt_fe_client_ctx *client_ctx,
fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_SESSION_REQ;
fe_msg.session_req = &sess_req;
- MGMTD_FE_CLIENT_DBG(
- "Sending SESSION_REQ message for %s session %llu to MGMTD Frontend server",
- create ? "creating" : "destroying",
- (unsigned long long)session->client_id);
+ debug_fe_client("Sending SESSION_REQ %s message for client-id %" PRIu64,
+ create ? "create" : "destroy", session->client_id);
- return mgmt_fe_client_send_msg(client_ctx, &fe_msg);
+ return mgmt_fe_client_send_msg(client, &fe_msg, true);
}
-static int
-mgmt_fe_send_lockds_req(struct mgmt_fe_client_ctx *client_ctx,
- struct mgmt_fe_client_session *session, bool lock,
- uint64_t req_id, Mgmtd__DatastoreId ds_id)
+int mgmt_fe_send_lockds_req(struct mgmt_fe_client *client, uint64_t session_id,
+ uint64_t req_id, Mgmtd__DatastoreId ds_id,
+ bool lock, bool scok)
{
(void)req_id;
Mgmtd__FeMessage fe_msg;
Mgmtd__FeLockDsReq lockds_req;
mgmtd__fe_lock_ds_req__init(&lockds_req);
- lockds_req.session_id = session->session_id;
+ lockds_req.session_id = session_id;
lockds_req.req_id = req_id;
lockds_req.ds_id = ds_id;
lockds_req.lock = lock;
@@ -272,17 +181,14 @@ mgmt_fe_send_lockds_req(struct mgmt_fe_client_ctx *client_ctx,
fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REQ;
fe_msg.lockds_req = &lockds_req;
- MGMTD_FE_CLIENT_DBG(
- "Sending %sLOCK_REQ message for Ds:%d session %llu to MGMTD Frontend server",
- lock ? "" : "UN", ds_id,
- (unsigned long long)session->client_id);
+ debug_fe_client("Sending LOCKDS_REQ (%sLOCK) message for DS:%s session-id %" PRIu64,
+ lock ? "" : "UN", dsid2name(ds_id), session_id);
+
- return mgmt_fe_client_send_msg(client_ctx, &fe_msg);
+ return mgmt_fe_client_send_msg(client, &fe_msg, scok);
}
-static int
-mgmt_fe_send_setcfg_req(struct mgmt_fe_client_ctx *client_ctx,
- struct mgmt_fe_client_session *session,
+int mgmt_fe_send_setcfg_req(struct mgmt_fe_client *client, uint64_t session_id,
uint64_t req_id, Mgmtd__DatastoreId ds_id,
Mgmtd__YangCfgDataReq **data_req, int num_data_reqs,
bool implicit_commit, Mgmtd__DatastoreId dst_ds_id)
@@ -292,7 +198,7 @@ mgmt_fe_send_setcfg_req(struct mgmt_fe_client_ctx *client_ctx,
Mgmtd__FeSetConfigReq setcfg_req;
mgmtd__fe_set_config_req__init(&setcfg_req);
- setcfg_req.session_id = session->session_id;
+ setcfg_req.session_id = session_id;
setcfg_req.ds_id = ds_id;
setcfg_req.req_id = req_id;
setcfg_req.data = data_req;
@@ -304,26 +210,25 @@ mgmt_fe_send_setcfg_req(struct mgmt_fe_client_ctx *client_ctx,
fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_SETCFG_REQ;
fe_msg.setcfg_req = &setcfg_req;
- MGMTD_FE_CLIENT_DBG(
- "Sending SET_CONFIG_REQ message for Ds:%d session %llu (#xpaths:%d) to MGMTD Frontend server",
- ds_id, (unsigned long long)session->client_id, num_data_reqs);
+ debug_fe_client("Sending SET_CONFIG_REQ message for DS:%s session-id %" PRIu64
+ " (#xpaths:%d)",
+ dsid2name(ds_id), session_id, num_data_reqs);
- return mgmt_fe_client_send_msg(client_ctx, &fe_msg);
+ return mgmt_fe_client_send_msg(client, &fe_msg, false);
}
-static int mgmt_fe_send_commitcfg_req(struct mgmt_fe_client_ctx *client_ctx,
- struct mgmt_fe_client_session *session,
- uint64_t req_id,
- Mgmtd__DatastoreId src_ds_id,
- Mgmtd__DatastoreId dest_ds_id,
- bool validate_only, bool abort)
+int mgmt_fe_send_commitcfg_req(struct mgmt_fe_client *client,
+ uint64_t session_id, uint64_t req_id,
+ Mgmtd__DatastoreId src_ds_id,
+ Mgmtd__DatastoreId dest_ds_id,
+ bool validate_only, bool abort)
{
(void)req_id;
Mgmtd__FeMessage fe_msg;
Mgmtd__FeCommitConfigReq commitcfg_req;
mgmtd__fe_commit_config_req__init(&commitcfg_req);
- commitcfg_req.session_id = session->session_id;
+ commitcfg_req.session_id = session_id;
commitcfg_req.src_ds_id = src_ds_id;
commitcfg_req.dst_ds_id = dest_ds_id;
commitcfg_req.req_id = req_id;
@@ -334,83 +239,52 @@ static int mgmt_fe_send_commitcfg_req(struct mgmt_fe_client_ctx *client_ctx,
fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REQ;
fe_msg.commcfg_req = &commitcfg_req;
- MGMTD_FE_CLIENT_DBG(
- "Sending COMMIT_CONFIG_REQ message for Src-Ds:%d, Dst-Ds:%d session %llu to MGMTD Frontend server",
- src_ds_id, dest_ds_id, (unsigned long long)session->client_id);
+ debug_fe_client("Sending COMMIT_CONFIG_REQ message for Src-DS:%s, Dst-DS:%s session-id %" PRIu64,
+ dsid2name(src_ds_id), dsid2name(dest_ds_id), session_id);
- return mgmt_fe_client_send_msg(client_ctx, &fe_msg);
+ return mgmt_fe_client_send_msg(client, &fe_msg, false);
}
-static int
-mgmt_fe_send_getcfg_req(struct mgmt_fe_client_ctx *client_ctx,
- struct mgmt_fe_client_session *session,
- uint64_t req_id, Mgmtd__DatastoreId ds_id,
- Mgmtd__YangGetDataReq * data_req[],
- int num_data_reqs)
+int mgmt_fe_send_get_req(struct mgmt_fe_client *client, uint64_t session_id,
+ uint64_t req_id, bool is_config,
+ Mgmtd__DatastoreId ds_id,
+ Mgmtd__YangGetDataReq *data_req[], int num_data_reqs)
{
(void)req_id;
Mgmtd__FeMessage fe_msg;
- Mgmtd__FeGetConfigReq getcfg_req;
+ Mgmtd__FeGetReq getcfg_req;
- mgmtd__fe_get_config_req__init(&getcfg_req);
- getcfg_req.session_id = session->session_id;
+ mgmtd__fe_get_req__init(&getcfg_req);
+ getcfg_req.session_id = session_id;
+ getcfg_req.config = is_config;
getcfg_req.ds_id = ds_id;
getcfg_req.req_id = req_id;
getcfg_req.data = data_req;
getcfg_req.n_data = (size_t)num_data_reqs;
mgmtd__fe_message__init(&fe_msg);
- fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_GETCFG_REQ;
- fe_msg.getcfg_req = &getcfg_req;
-
- MGMTD_FE_CLIENT_DBG(
- "Sending GET_CONFIG_REQ message for Ds:%d session %llu (#xpaths:%d) to MGMTD Frontend server",
- ds_id, (unsigned long long)session->client_id, num_data_reqs);
-
- return mgmt_fe_client_send_msg(client_ctx, &fe_msg);
-}
-
-static int
-mgmt_fe_send_getdata_req(struct mgmt_fe_client_ctx *client_ctx,
- struct mgmt_fe_client_session *session,
- uint64_t req_id, Mgmtd__DatastoreId ds_id,
- Mgmtd__YangGetDataReq * data_req[],
- int num_data_reqs)
-{
- (void)req_id;
- Mgmtd__FeMessage fe_msg;
- Mgmtd__FeGetDataReq getdata_req;
-
- mgmtd__fe_get_data_req__init(&getdata_req);
- getdata_req.session_id = session->session_id;
- getdata_req.ds_id = ds_id;
- getdata_req.req_id = req_id;
- getdata_req.data = data_req;
- getdata_req.n_data = (size_t)num_data_reqs;
-
- mgmtd__fe_message__init(&fe_msg);
- fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_GETDATA_REQ;
- fe_msg.getdata_req = &getdata_req;
+ fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_GET_REQ;
+ fe_msg.get_req = &getcfg_req;
- MGMTD_FE_CLIENT_DBG(
- "Sending GET_CONFIG_REQ message for Ds:%d session %llu (#xpaths:%d) to MGMTD Frontend server",
- ds_id, (unsigned long long)session->client_id, num_data_reqs);
+ debug_fe_client("Sending GET_REQ (iscfg %d) message for DS:%s session-id %" PRIu64
+ " (#xpaths:%d)",
+ is_config, dsid2name(ds_id), session_id, num_data_reqs);
- return mgmt_fe_client_send_msg(client_ctx, &fe_msg);
+ return mgmt_fe_client_send_msg(client, &fe_msg, false);
}
-static int mgmt_fe_send_regnotify_req(
- struct mgmt_fe_client_ctx *client_ctx,
- struct mgmt_fe_client_session *session, uint64_t req_id,
- Mgmtd__DatastoreId ds_id, bool register_req,
- Mgmtd__YangDataXPath * data_req[], int num_data_reqs)
+int mgmt_fe_send_regnotify_req(struct mgmt_fe_client *client,
+ uint64_t session_id, uint64_t req_id,
+ Mgmtd__DatastoreId ds_id, bool register_req,
+ Mgmtd__YangDataXPath *data_req[],
+ int num_data_reqs)
{
(void)req_id;
Mgmtd__FeMessage fe_msg;
Mgmtd__FeRegisterNotifyReq regntfy_req;
mgmtd__fe_register_notify_req__init(&regntfy_req);
- regntfy_req.session_id = session->session_id;
+ regntfy_req.session_id = session_id;
regntfy_req.ds_id = ds_id;
regntfy_req.register_req = register_req;
regntfy_req.data_xpath = data_req;
@@ -420,12 +294,44 @@ static int mgmt_fe_send_regnotify_req(
fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_REGNOTIFY_REQ;
fe_msg.regnotify_req = &regntfy_req;
- return mgmt_fe_client_send_msg(client_ctx, &fe_msg);
+ return mgmt_fe_client_send_msg(client, &fe_msg, false);
+}
+
+/*
+ * Send get-data request.
+ */
+int mgmt_fe_send_get_data_req(struct mgmt_fe_client *client,
+ uint64_t session_id, uint64_t req_id,
+ uint8_t datastore, LYD_FORMAT result_type,
+ uint8_t flags, uint8_t defaults, const char *xpath)
+{
+ struct mgmt_msg_get_data *msg;
+ size_t xplen = strlen(xpath);
+ int ret;
+
+ msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_get_data, xplen + 1,
+ MTYPE_MSG_NATIVE_GET_DATA);
+ msg->refer_id = session_id;
+ msg->req_id = req_id;
+ msg->code = MGMT_MSG_CODE_GET_DATA;
+ msg->result_type = result_type;
+ msg->flags = flags;
+ msg->defaults = defaults;
+ msg->datastore = datastore;
+ strlcpy(msg->xpath, xpath, xplen + 1);
+
+ debug_fe_client("Sending GET_DATA_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_ctx *client_ctx,
- Mgmtd__FeMessage *fe_msg)
+
+static int mgmt_fe_client_handle_msg(struct mgmt_fe_client *client,
+ Mgmtd__FeMessage *fe_msg)
{
struct mgmt_fe_client_session *session = NULL;
@@ -435,119 +341,92 @@ mgmt_fe_client_handle_msg(struct mgmt_fe_client_ctx *client_ctx,
*/
switch ((int)fe_msg->message_case) {
case MGMTD__FE_MESSAGE__MESSAGE_SESSION_REPLY:
- if (fe_msg->session_reply->create
- && fe_msg->session_reply->has_client_conn_id) {
- MGMTD_FE_CLIENT_DBG(
- "Got Session Create Reply Msg for client-id %llu with session-id: %llu.",
- (unsigned long long)
+ if (fe_msg->session_reply->create &&
+ fe_msg->session_reply->has_client_conn_id) {
+ debug_fe_client("Got SESSION_REPLY (create) for client-id %" PRIu64
+ " with session-id: %" PRIu64,
fe_msg->session_reply->client_conn_id,
- (unsigned long long)
fe_msg->session_reply->session_id);
session = mgmt_fe_find_session_by_client_id(
- client_ctx,
- fe_msg->session_reply->client_conn_id);
+ client, fe_msg->session_reply->client_conn_id);
if (session && fe_msg->session_reply->success) {
- MGMTD_FE_CLIENT_DBG(
- "Session Create for client-id %llu successful.",
- (unsigned long long)
+ debug_fe_client("Session Created for client-id %" PRIu64,
fe_msg->session_reply
->client_conn_id);
session->session_id =
fe_msg->session_reply->session_id;
} else {
- MGMTD_FE_CLIENT_ERR(
- "Session Create for client-id %llu failed.",
- (unsigned long long)
- fe_msg->session_reply
- ->client_conn_id);
+ log_err_fe_client(
+ "Session Create failed for client-id %" PRIu64,
+ fe_msg->session_reply->client_conn_id);
}
} else if (!fe_msg->session_reply->create) {
- MGMTD_FE_CLIENT_DBG(
- "Got Session Destroy Reply Msg for session-id %llu",
- (unsigned long long)
+ debug_fe_client("Got SESSION_REPLY (destroy) for session-id %" PRIu64,
fe_msg->session_reply->session_id);
session = mgmt_fe_find_session_by_session_id(
- client_ctx, fe_msg->session_req->session_id);
+ client, fe_msg->session_req->session_id);
}
- if (session && session->client_ctx
- && session->client_ctx->client_params
- .client_session_notify)
- (*session->client_ctx->client_params
- .client_session_notify)(
- (uintptr_t)client_ctx,
- client_ctx->client_params.user_data,
- session->client_id,
+ /* The session state may be deleted by the callback */
+ if (session && session->client &&
+ session->client->cbs.client_session_notify)
+ (*session->client->cbs.client_session_notify)(
+ client, client->user_data, session->client_id,
fe_msg->session_reply->create,
fe_msg->session_reply->success,
- (uintptr_t)session, session->user_ctx);
+ fe_msg->session_reply->session_id,
+ session->user_ctx);
break;
case MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REPLY:
- MGMTD_FE_CLIENT_DBG(
- "Got LockDs Reply Msg for session-id %llu",
- (unsigned long long)
+ debug_fe_client("Got LOCKDS_REPLY for session-id %" PRIu64,
fe_msg->lockds_reply->session_id);
session = mgmt_fe_find_session_by_session_id(
- client_ctx, fe_msg->lockds_reply->session_id);
-
- if (session && session->client_ctx
- && session->client_ctx->client_params
- .lock_ds_notify)
- (*session->client_ctx->client_params
- .lock_ds_notify)(
- (uintptr_t)client_ctx,
- client_ctx->client_params.user_data,
- session->client_id, (uintptr_t)session,
- session->user_ctx,
- fe_msg->lockds_reply->req_id,
+ client, fe_msg->lockds_reply->session_id);
+
+ if (session && session->client &&
+ session->client->cbs.lock_ds_notify)
+ (*session->client->cbs.lock_ds_notify)(
+ client, client->user_data, session->client_id,
+ fe_msg->lockds_reply->session_id,
+ session->user_ctx, fe_msg->lockds_reply->req_id,
fe_msg->lockds_reply->lock,
fe_msg->lockds_reply->success,
fe_msg->lockds_reply->ds_id,
fe_msg->lockds_reply->error_if_any);
break;
case MGMTD__FE_MESSAGE__MESSAGE_SETCFG_REPLY:
- MGMTD_FE_CLIENT_DBG(
- "Got Set Config Reply Msg for session-id %llu",
- (unsigned long long)
+ debug_fe_client("Got SETCFG_REPLY for session-id %" PRIu64,
fe_msg->setcfg_reply->session_id);
session = mgmt_fe_find_session_by_session_id(
- client_ctx, fe_msg->setcfg_reply->session_id);
-
- if (session && session->client_ctx
- && session->client_ctx->client_params
- .set_config_notify)
- (*session->client_ctx->client_params
- .set_config_notify)(
- (uintptr_t)client_ctx,
- client_ctx->client_params.user_data,
- session->client_id, (uintptr_t)session,
- session->user_ctx,
- fe_msg->setcfg_reply->req_id,
+ client, fe_msg->setcfg_reply->session_id);
+
+ if (session && session->client &&
+ session->client->cbs.set_config_notify)
+ (*session->client->cbs.set_config_notify)(
+ client, client->user_data, session->client_id,
+ fe_msg->setcfg_reply->session_id,
+ session->user_ctx, fe_msg->setcfg_reply->req_id,
fe_msg->setcfg_reply->success,
fe_msg->setcfg_reply->ds_id,
+ fe_msg->setcfg_reply->implicit_commit,
fe_msg->setcfg_reply->error_if_any);
break;
case MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REPLY:
- MGMTD_FE_CLIENT_DBG(
- "Got Commit Config Reply Msg for session-id %llu",
- (unsigned long long)
+ debug_fe_client("Got COMMCFG_REPLY for session-id %" PRIu64,
fe_msg->commcfg_reply->session_id);
session = mgmt_fe_find_session_by_session_id(
- client_ctx, fe_msg->commcfg_reply->session_id);
-
- if (session && session->client_ctx
- && session->client_ctx->client_params
- .commit_config_notify)
- (*session->client_ctx->client_params
- .commit_config_notify)(
- (uintptr_t)client_ctx,
- client_ctx->client_params.user_data,
- session->client_id, (uintptr_t)session,
+ client, fe_msg->commcfg_reply->session_id);
+
+ if (session && session->client &&
+ session->client->cbs.commit_config_notify)
+ (*session->client->cbs.commit_config_notify)(
+ client, client->user_data, session->client_id,
+ fe_msg->commcfg_reply->session_id,
session->user_ctx,
fe_msg->commcfg_reply->req_id,
fe_msg->commcfg_reply->success,
@@ -556,72 +435,33 @@ mgmt_fe_client_handle_msg(struct mgmt_fe_client_ctx *client_ctx,
fe_msg->commcfg_reply->validate_only,
fe_msg->commcfg_reply->error_if_any);
break;
- case MGMTD__FE_MESSAGE__MESSAGE_GETCFG_REPLY:
- MGMTD_FE_CLIENT_DBG(
- "Got Get Config Reply Msg for session-id %llu",
- (unsigned long long)
- fe_msg->getcfg_reply->session_id);
-
- session = mgmt_fe_find_session_by_session_id(
- client_ctx, fe_msg->getcfg_reply->session_id);
-
- if (session && session->client_ctx
- && session->client_ctx->client_params
- .get_data_notify)
- (*session->client_ctx->client_params
- .get_data_notify)(
- (uintptr_t)client_ctx,
- client_ctx->client_params.user_data,
- session->client_id, (uintptr_t)session,
- session->user_ctx,
- fe_msg->getcfg_reply->req_id,
- fe_msg->getcfg_reply->success,
- fe_msg->getcfg_reply->ds_id,
- fe_msg->getcfg_reply->data
- ? fe_msg->getcfg_reply->data->data
+ case MGMTD__FE_MESSAGE__MESSAGE_GET_REPLY:
+ debug_fe_client("Got GET_REPLY for session-id %" PRIu64,
+ fe_msg->get_reply->session_id);
+
+ session =
+ mgmt_fe_find_session_by_session_id(client,
+ fe_msg->get_reply
+ ->session_id);
+
+ if (session && session->client &&
+ session->client->cbs.get_data_notify)
+ (*session->client->cbs.get_data_notify)(
+ client, client->user_data, session->client_id,
+ fe_msg->get_reply->session_id,
+ session->user_ctx, fe_msg->get_reply->req_id,
+ fe_msg->get_reply->success,
+ fe_msg->get_reply->ds_id,
+ fe_msg->get_reply->data
+ ? fe_msg->get_reply->data->data
: NULL,
- fe_msg->getcfg_reply->data
- ? fe_msg->getcfg_reply->data->n_data
+ fe_msg->get_reply->data
+ ? fe_msg->get_reply->data->n_data
: 0,
- fe_msg->getcfg_reply->data
- ? fe_msg->getcfg_reply->data
- ->next_indx
+ fe_msg->get_reply->data
+ ? fe_msg->get_reply->data->next_indx
: 0,
- fe_msg->getcfg_reply->error_if_any);
- break;
- case MGMTD__FE_MESSAGE__MESSAGE_GETDATA_REPLY:
- MGMTD_FE_CLIENT_DBG(
- "Got Get Data Reply Msg for session-id %llu",
- (unsigned long long)
- fe_msg->getdata_reply->session_id);
-
- session = mgmt_fe_find_session_by_session_id(
- client_ctx, fe_msg->getdata_reply->session_id);
-
- if (session && session->client_ctx
- && session->client_ctx->client_params
- .get_data_notify)
- (*session->client_ctx->client_params
- .get_data_notify)(
- (uintptr_t)client_ctx,
- client_ctx->client_params.user_data,
- session->client_id, (uintptr_t)session,
- session->user_ctx,
- fe_msg->getdata_reply->req_id,
- fe_msg->getdata_reply->success,
- fe_msg->getdata_reply->ds_id,
- fe_msg->getdata_reply->data
- ? fe_msg->getdata_reply->data->data
- : NULL,
- fe_msg->getdata_reply->data
- ? fe_msg->getdata_reply->data
- ->n_data
- : 0,
- fe_msg->getdata_reply->data
- ? fe_msg->getdata_reply->data
- ->next_indx
- : 0,
- fe_msg->getdata_reply->error_if_any);
+ fe_msg->get_reply->error_if_any);
break;
case MGMTD__FE_MESSAGE__MESSAGE_NOTIFY_DATA_REQ:
case MGMTD__FE_MESSAGE__MESSAGE_REGNOTIFY_REQ:
@@ -638,8 +478,7 @@ mgmt_fe_client_handle_msg(struct mgmt_fe_client_ctx *client_ctx,
case MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REQ:
case MGMTD__FE_MESSAGE__MESSAGE_SETCFG_REQ:
case MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REQ:
- case MGMTD__FE_MESSAGE__MESSAGE_GETCFG_REQ:
- case MGMTD__FE_MESSAGE__MESSAGE_GETDATA_REQ:
+ case MGMTD__FE_MESSAGE__MESSAGE_GET_REQ:
case MGMTD__FE_MESSAGE__MESSAGE__NOT_SET:
default:
/*
@@ -654,397 +493,351 @@ mgmt_fe_client_handle_msg(struct mgmt_fe_client_ctx *client_ctx,
return 0;
}
-static void mgmt_fe_client_process_msg(void *user_ctx, uint8_t *data,
- size_t len)
+/*
+ * Handle a native encoded message
+ */
+static void fe_client_handle_native_msg(struct mgmt_fe_client *client,
+ struct mgmt_msg_header *msg,
+ size_t msg_len)
{
- struct mgmt_fe_client_ctx *client_ctx = user_ctx;
- Mgmtd__FeMessage *fe_msg;
-
- fe_msg = mgmtd__fe_message__unpack(NULL, len, data);
- if (!fe_msg) {
- MGMTD_FE_CLIENT_DBG("Failed to decode %zu bytes from server.",
- len);
+ struct mgmt_fe_client_session *session = NULL;
+ struct mgmt_msg_notify_data *notify_msg;
+ struct mgmt_msg_tree_data *tree_msg;
+ struct mgmt_msg_error *err_msg;
+ const char *data = NULL;
+ size_t dlen;
+
+ debug_fe_client("Got native message for session-id %" PRIu64,
+ msg->refer_id);
+
+ session = mgmt_fe_find_session_by_session_id(client, msg->refer_id);
+ if (!session || !session->client) {
+ log_err_fe_client("No session for received native msg session-id %" PRIu64,
+ msg->refer_id);
return;
}
- MGMTD_FE_CLIENT_DBG(
- "Decoded %zu bytes of message(msg: %u/%u) from server", len,
- fe_msg->message_case, fe_msg->message_case);
- (void)mgmt_fe_client_handle_msg(client_ctx, fe_msg);
- mgmtd__fe_message__free_unpacked(fe_msg, NULL);
-}
-static void mgmt_fe_client_proc_msgbufs(struct event *thread)
-{
- struct mgmt_fe_client_ctx *client_ctx;
+ switch (msg->code) {
+ case MGMT_MSG_CODE_ERROR:
+ if (!session->client->cbs.error_notify)
+ return;
- client_ctx = (struct mgmt_fe_client_ctx *)EVENT_ARG(thread);
- if (mgmt_msg_procbufs(&client_ctx->mstate, mgmt_fe_client_process_msg,
- client_ctx, mgmt_debug_fe_client))
- mgmt_fe_client_register_event(client_ctx, MGMTD_FE_PROC_MSG);
-}
+ err_msg = (typeof(err_msg))msg;
+ if (!MGMT_MSG_VALIDATE_NUL_TERM(err_msg, msg_len)) {
+ log_err_fe_client("Corrupt error msg recv");
+ return;
+ }
+ session->client->cbs.error_notify(client, client->user_data,
+ session->client_id,
+ msg->refer_id,
+ session->user_ctx,
+ msg->req_id, err_msg->error,
+ err_msg->errstr);
+ break;
+ case MGMT_MSG_CODE_TREE_DATA:
+ if (!session->client->cbs.get_tree_notify)
+ return;
+
+ tree_msg = (typeof(tree_msg))msg;
+ if (msg_len < sizeof(*tree_msg)) {
+ log_err_fe_client("Corrupt tree-data msg recv");
+ return;
+ }
+ session->client->cbs.get_tree_notify(client, client->user_data,
+ session->client_id,
+ msg->refer_id,
+ session->user_ctx,
+ msg->req_id,
+ MGMTD_DS_OPERATIONAL,
+ tree_msg->result_type,
+ tree_msg->result,
+ msg_len - sizeof(*tree_msg),
+ tree_msg->partial_error);
+ break;
+ case MGMT_MSG_CODE_NOTIFY:
+ if (!session->client->cbs.async_notification)
+ return;
+
+ notify_msg = (typeof(notify_msg))msg;
+ if (msg_len < sizeof(*notify_msg)) {
+ log_err_fe_client("Corrupt notify-data msg recv");
+ return;
+ }
-static void mgmt_fe_client_read(struct event *thread)
-{
- struct mgmt_fe_client_ctx *client_ctx;
- enum mgmt_msg_rsched rv;
+ data = mgmt_msg_native_data_decode(notify_msg, msg_len);
+ if (!data) {
+ log_err_fe_client("Corrupt error msg recv");
+ return;
+ }
+ dlen = mgmt_msg_native_data_len_decode(notify_msg, msg_len);
+ if (notify_msg->result_type != LYD_JSON)
+ data = yang_convert_lyd_format(data, dlen,
+ notify_msg->result_type,
+ LYD_JSON, true);
+ if (!data) {
+ log_err_fe_client("Can't convert format %d to JSON",
+ notify_msg->result_type);
+ return;
+ }
- client_ctx = (struct mgmt_fe_client_ctx *)EVENT_ARG(thread);
+ session->client->cbs.async_notification(client,
+ client->user_data,
+ session->client_id,
+ msg->refer_id,
+ session->user_ctx, data);
- rv = mgmt_msg_read(&client_ctx->mstate, client_ctx->conn_fd,
- mgmt_debug_fe_client);
- if (rv == MSR_DISCONNECT) {
- mgmt_fe_server_disconnect(client_ctx, true);
- return;
+ if (notify_msg->result_type != LYD_JSON)
+ darr_free(data);
+ break;
+ default:
+ log_err_fe_client("unknown native message session-id %" PRIu64
+ " req-id %" PRIu64 " code %u",
+ msg->refer_id, msg->req_id, msg->code);
+ break;
}
- if (rv == MSR_SCHED_BOTH)
- mgmt_fe_client_register_event(client_ctx, MGMTD_FE_PROC_MSG);
- mgmt_fe_client_register_event(client_ctx, MGMTD_FE_CONN_READ);
}
-static void mgmt_fe_server_connect(struct mgmt_fe_client_ctx *client_ctx)
+static void mgmt_fe_client_process_msg(uint8_t version, uint8_t *data,
+ size_t len, struct msg_conn *conn)
{
- const char *dbgtag = mgmt_debug_fe_client ? "FE-client" : NULL;
+ struct mgmt_fe_client *client;
+ struct msg_client *msg_client;
+ Mgmtd__FeMessage *fe_msg;
- assert(client_ctx->conn_fd == -1);
- client_ctx->conn_fd = mgmt_msg_connect(
- MGMTD_FE_SERVER_PATH, MGMTD_SOCKET_FE_SEND_BUF_SIZE,
- MGMTD_SOCKET_FE_RECV_BUF_SIZE, dbgtag);
+ msg_client = container_of(conn, struct msg_client, conn);
+ client = container_of(msg_client, struct mgmt_fe_client, client);
- /* Send REGISTER_REQ message */
- if (client_ctx->conn_fd == -1 ||
- mgmt_fe_send_register_req(client_ctx) != 0) {
- mgmt_fe_server_disconnect(client_ctx, true);
+ if (version == MGMT_MSG_VERSION_NATIVE) {
+ struct mgmt_msg_header *msg = (typeof(msg))data;
+
+ if (len >= sizeof(*msg))
+ fe_client_handle_native_msg(client, msg, len);
+ else
+ log_err_fe_client("native message to FE client %s too short %zu",
+ client->name, len);
return;
}
- /* Start reading from the socket */
- mgmt_fe_client_register_event(client_ctx, MGMTD_FE_CONN_READ);
-
- /* Notify client through registered callback (if any) */
- if (client_ctx->client_params.client_connect_notify)
- (void)(*client_ctx->client_params.client_connect_notify)(
- (uintptr_t)client_ctx,
- client_ctx->client_params.user_data, true);
-}
-
-
-static void mgmt_fe_client_conn_timeout(struct event *thread)
-{
- mgmt_fe_server_connect(EVENT_ARG(thread));
-}
-
-static void
-mgmt_fe_client_register_event(struct mgmt_fe_client_ctx *client_ctx,
- enum mgmt_fe_event event)
-{
- struct timeval tv = {0};
-
- switch (event) {
- case MGMTD_FE_CONN_READ:
- event_add_read(client_ctx->tm, mgmt_fe_client_read,
- client_ctx, client_ctx->conn_fd,
- &client_ctx->conn_read_ev);
- break;
- case MGMTD_FE_CONN_WRITE:
- event_add_write(client_ctx->tm, mgmt_fe_client_write,
- client_ctx, client_ctx->conn_fd,
- &client_ctx->conn_write_ev);
- break;
- case MGMTD_FE_PROC_MSG:
- tv.tv_usec = MGMTD_FE_MSG_PROC_DELAY_USEC;
- event_add_timer_tv(client_ctx->tm,
- mgmt_fe_client_proc_msgbufs, client_ctx,
- &tv, &client_ctx->msg_proc_ev);
- break;
- case MGMTD_FE_CONN_WRITES_ON:
- event_add_timer_msec(
- client_ctx->tm, mgmt_fe_client_resume_writes,
- client_ctx, MGMTD_FE_MSG_WRITE_DELAY_MSEC,
- &client_ctx->conn_writes_on);
- break;
- case MGMTD_FE_SERVER:
- assert(!"mgmt_fe_client_ctx_post_event called incorrectly");
- break;
+ fe_msg = mgmtd__fe_message__unpack(NULL, len, data);
+ if (!fe_msg) {
+ debug_fe_client("Failed to decode %zu bytes from server.", len);
+ return;
}
+ debug_fe_client("Decoded %zu bytes of message(msg: %u/%u) from server",
+ len, fe_msg->message_case, fe_msg->message_case);
+ (void)mgmt_fe_client_handle_msg(client, fe_msg);
+ mgmtd__fe_message__free_unpacked(fe_msg, NULL);
}
-static void mgmt_fe_client_schedule_conn_retry(
- struct mgmt_fe_client_ctx *client_ctx, unsigned long intvl_secs)
+static int _notify_connect_disconnect(struct msg_client *msg_client,
+ bool connected)
{
- MGMTD_FE_CLIENT_DBG(
- "Scheduling MGMTD Frontend server connection retry after %lu seconds",
- intvl_secs);
- event_add_timer(client_ctx->tm, mgmt_fe_client_conn_timeout,
- (void *)client_ctx, intvl_secs,
- &client_ctx->conn_retry_tmr);
-}
+ struct mgmt_fe_client *client =
+ container_of(msg_client, struct mgmt_fe_client, client);
+ struct mgmt_fe_client_session *session;
+ int ret;
-/*
- * Initialize library and try connecting with MGMTD.
- */
-uintptr_t mgmt_fe_client_lib_init(struct mgmt_fe_client_params *params,
- struct event_loop *master_thread)
-{
- assert(master_thread && params && strlen(params->name)
- && !mgmt_fe_client_ctx.tm);
+ /* Send REGISTER_REQ message */
+ if (connected) {
+ if ((ret = mgmt_fe_send_register_req(client)) != 0)
+ return ret;
+ }
- mgmt_fe_client_ctx.tm = master_thread;
- memcpy(&mgmt_fe_client_ctx.client_params, params,
- sizeof(mgmt_fe_client_ctx.client_params));
- if (!mgmt_fe_client_ctx.client_params.conn_retry_intvl_sec)
- mgmt_fe_client_ctx.client_params.conn_retry_intvl_sec =
- MGMTD_FE_DEFAULT_CONN_RETRY_INTVL_SEC;
+ /* Walk list of sessions for this FE client deleting them */
+ if (!connected && mgmt_sessions_count(&client->sessions)) {
+ debug_fe_client("Cleaning up existing sessions");
- mgmt_msg_init(&mgmt_fe_client_ctx.mstate, MGMTD_FE_MAX_NUM_MSG_PROC,
- MGMTD_FE_MAX_NUM_MSG_WRITE, MGMTD_FE_MSG_MAX_LEN,
- "FE-client");
+ FOREACH_SESSION_IN_LIST (client, session) {
+ assert(session->client);
- mgmt_sessions_init(&mgmt_fe_client_ctx.client_sessions);
+ /* unlink from list first this avoids double free */
+ mgmt_sessions_del(&client->sessions, session);
- /* Start trying to connect to MGMTD frontend server immediately */
- mgmt_fe_client_schedule_conn_retry(&mgmt_fe_client_ctx, 1);
+ /* notify FE client the session is being deleted */
+ if (session->client->cbs.client_session_notify) {
+ (*session->client->cbs.client_session_notify)(
+ client, client->user_data,
+ session->client_id, false, true,
+ session->session_id, session->user_ctx);
+ }
- MGMTD_FE_CLIENT_DBG("Initialized client '%s'", params->name);
+ XFREE(MTYPE_MGMTD_FE_SESSION, session);
+ }
+ }
- return (uintptr_t)&mgmt_fe_client_ctx;
+ /* Notify FE client through registered callback (if any). */
+ if (client->cbs.client_connect_notify)
+ (void)(*client->cbs.client_connect_notify)(
+ client, client->user_data, connected);
+ return 0;
}
-/*
- * Create a new Session for a Frontend Client connection.
- */
-enum mgmt_result mgmt_fe_create_client_session(uintptr_t lib_hndl,
- uint64_t client_id,
- uintptr_t user_ctx)
+static int mgmt_fe_client_notify_connect(struct msg_client *client)
{
- struct mgmt_fe_client_ctx *client_ctx;
- struct mgmt_fe_client_session *session;
-
- client_ctx = (struct mgmt_fe_client_ctx *)lib_hndl;
- if (!client_ctx)
- return MGMTD_INVALID_PARAM;
-
- session = XCALLOC(MTYPE_MGMTD_FE_SESSION,
- sizeof(struct mgmt_fe_client_session));
- assert(session);
- session->user_ctx = user_ctx;
- session->client_id = client_id;
- session->client_ctx = client_ctx;
- session->session_id = 0;
-
- if (mgmt_fe_send_session_req(client_ctx, session, true) != 0) {
- XFREE(MTYPE_MGMTD_FE_SESSION, session);
- return MGMTD_INTERNAL_ERROR;
- }
- mgmt_sessions_add_tail(&client_ctx->client_sessions, session);
-
- return MGMTD_SUCCESS;
+ return _notify_connect_disconnect(client, true);
}
-/*
- * Delete an existing Session for a Frontend Client connection.
- */
-enum mgmt_result mgmt_fe_destroy_client_session(uintptr_t lib_hndl,
- uint64_t client_id)
+static int mgmt_fe_client_notify_disconnect(struct msg_conn *conn)
{
- struct mgmt_fe_client_ctx *client_ctx;
- struct mgmt_fe_client_session *session;
-
- client_ctx = (struct mgmt_fe_client_ctx *)lib_hndl;
- if (!client_ctx)
- return MGMTD_INVALID_PARAM;
-
- session = mgmt_fe_find_session_by_client_id(client_ctx, client_id);
- if (!session || session->client_ctx != client_ctx)
- return MGMTD_INVALID_PARAM;
-
- if (session->session_id &&
- mgmt_fe_send_session_req(client_ctx, session, false) != 0)
- MGMTD_FE_CLIENT_ERR(
- "Failed to send session destroy request for the session-id %lu",
- (unsigned long)session->session_id);
-
- mgmt_sessions_del(&client_ctx->client_sessions, session);
- XFREE(MTYPE_MGMTD_FE_SESSION, session);
+ struct msg_client *client = container_of(conn, struct msg_client, conn);
- return MGMTD_SUCCESS;
+ return _notify_connect_disconnect(client, false);
}
-static void mgmt_fe_destroy_client_sessions(uintptr_t lib_hndl)
+static void mgmt_debug_client_fe_set(uint32_t mode, bool set)
{
- struct mgmt_fe_client_ctx *client_ctx;
- struct mgmt_fe_client_session *session;
+ DEBUG_FLAGS_SET(&mgmt_dbg_fe_client, mode, set);
- client_ctx = (struct mgmt_fe_client_ctx *)lib_hndl;
- if (!client_ctx)
+ if (!__fe_client)
return;
- FOREACH_SESSION_IN_LIST (client_ctx, session)
- mgmt_fe_destroy_client_session(lib_hndl, session->client_id);
+ __fe_client->client.conn.debug = DEBUG_MODE_CHECK(&mgmt_dbg_fe_client,
+ DEBUG_MODE_ALL);
}
-/*
- * Send UN/LOCK_DS_REQ to MGMTD for a specific Datastore DS.
- */
-enum mgmt_result mgmt_fe_lock_ds(uintptr_t lib_hndl, uintptr_t session_id,
- uint64_t req_id, Mgmtd__DatastoreId ds_id,
- bool lock_ds)
+DEFPY(debug_mgmt_client_fe, debug_mgmt_client_fe_cmd,
+ "[no] debug mgmt client frontend",
+ NO_STR DEBUG_STR MGMTD_STR
+ "client\n"
+ "frontend\n")
{
- struct mgmt_fe_client_ctx *client_ctx;
- struct mgmt_fe_client_session *session;
+ mgmt_debug_client_fe_set(DEBUG_NODE2MODE(vty->node), !no);
- client_ctx = (struct mgmt_fe_client_ctx *)lib_hndl;
- if (!client_ctx)
- return MGMTD_INVALID_PARAM;
+ return CMD_SUCCESS;
+}
- session = (struct mgmt_fe_client_session *)session_id;
- if (!session || session->client_ctx != client_ctx)
- return MGMTD_INVALID_PARAM;
+static int mgmt_debug_fe_client_config_write(struct vty *vty)
+{
+ if (DEBUG_MODE_CHECK(&mgmt_dbg_fe_client, DEBUG_MODE_CONF))
+ vty_out(vty, "debug mgmt client frontend\n");
- if (mgmt_fe_send_lockds_req(client_ctx, session, lock_ds, req_id,
- ds_id)
- != 0)
- return MGMTD_INTERNAL_ERROR;
+ return CMD_SUCCESS;
+}
- return MGMTD_SUCCESS;
+void mgmt_debug_fe_client_show_debug(struct vty *vty)
+{
+ if (debug_check_fe_client())
+ vty_out(vty, "debug mgmt client frontend\n");
}
+static struct debug_callbacks mgmt_dbg_fe_client_cbs = {
+ .debug_set_all = mgmt_debug_client_fe_set
+};
+
+static struct cmd_node mgmt_dbg_node = {
+ .name = "debug mgmt client frontend",
+ .node = MGMT_FE_DEBUG_NODE,
+ .prompt = "",
+ .config_write = mgmt_debug_fe_client_config_write,
+};
+
/*
- * Send SET_CONFIG_REQ to MGMTD for one or more config data(s).
+ * Initialize library and try connecting with MGMTD.
*/
-enum mgmt_result
-mgmt_fe_set_config_data(uintptr_t lib_hndl, uintptr_t session_id,
- uint64_t req_id, Mgmtd__DatastoreId ds_id,
- Mgmtd__YangCfgDataReq **config_req, int num_reqs,
- bool implicit_commit, Mgmtd__DatastoreId dst_ds_id)
+struct mgmt_fe_client *mgmt_fe_client_create(const char *client_name,
+ struct mgmt_fe_client_cbs *cbs,
+ uintptr_t user_data,
+ struct event_loop *event_loop)
{
- struct mgmt_fe_client_ctx *client_ctx;
- struct mgmt_fe_client_session *session;
+ struct mgmt_fe_client *client;
+ char server_path[MAXPATHLEN];
- client_ctx = (struct mgmt_fe_client_ctx *)lib_hndl;
- if (!client_ctx)
- return MGMTD_INVALID_PARAM;
+ if (__fe_client)
+ return NULL;
- session = (struct mgmt_fe_client_session *)session_id;
- if (!session || session->client_ctx != client_ctx)
- return MGMTD_INVALID_PARAM;
+ client = XCALLOC(MTYPE_MGMTD_FE_CLIENT, sizeof(*client));
+ __fe_client = client;
- if (mgmt_fe_send_setcfg_req(client_ctx, session, req_id, ds_id,
- config_req, num_reqs, implicit_commit,
- dst_ds_id)
- != 0)
- return MGMTD_INTERNAL_ERROR;
+ client->name = XSTRDUP(MTYPE_MGMTD_FE_CLIENT_NAME, client_name);
+ client->user_data = user_data;
+ if (cbs)
+ client->cbs = *cbs;
- return MGMTD_SUCCESS;
-}
+ mgmt_sessions_init(&client->sessions);
-/*
- * Send SET_CONFIG_REQ to MGMTD for one or more config data(s).
- */
-enum mgmt_result mgmt_fe_commit_config_data(uintptr_t lib_hndl,
- uintptr_t session_id,
- uint64_t req_id,
- Mgmtd__DatastoreId src_ds_id,
- Mgmtd__DatastoreId dst_ds_id,
- bool validate_only, bool abort)
-{
- struct mgmt_fe_client_ctx *client_ctx;
- struct mgmt_fe_client_session *session;
+ snprintf(server_path, sizeof(server_path), MGMTD_FE_SOCK_NAME);
- client_ctx = (struct mgmt_fe_client_ctx *)lib_hndl;
- if (!client_ctx)
- return MGMTD_INVALID_PARAM;
-
- session = (struct mgmt_fe_client_session *)session_id;
- if (!session || session->client_ctx != client_ctx)
- return MGMTD_INVALID_PARAM;
+ msg_client_init(&client->client, event_loop, server_path,
+ mgmt_fe_client_notify_connect,
+ mgmt_fe_client_notify_disconnect,
+ mgmt_fe_client_process_msg, MGMTD_FE_MAX_NUM_MSG_PROC,
+ MGMTD_FE_MAX_NUM_MSG_WRITE, MGMTD_FE_MAX_MSG_LEN, true,
+ "FE-client", debug_check_fe_client());
- if (mgmt_fe_send_commitcfg_req(client_ctx, session, req_id, src_ds_id,
- dst_ds_id, validate_only, abort)
- != 0)
- return MGMTD_INTERNAL_ERROR;
+ debug_fe_client("Initialized client '%s'", client_name);
- return MGMTD_SUCCESS;
+ return client;
}
-/*
- * Send GET_CONFIG_REQ to MGMTD for one or more config data item(s).
- */
-enum mgmt_result
-mgmt_fe_get_config_data(uintptr_t lib_hndl, uintptr_t session_id,
- uint64_t req_id, Mgmtd__DatastoreId ds_id,
- Mgmtd__YangGetDataReq * data_req[], int num_reqs)
+void mgmt_fe_client_lib_vty_init(void)
{
- struct mgmt_fe_client_ctx *client_ctx;
- struct mgmt_fe_client_session *session;
-
- client_ctx = (struct mgmt_fe_client_ctx *)lib_hndl;
- if (!client_ctx)
- return MGMTD_INVALID_PARAM;
+ debug_init(&mgmt_dbg_fe_client_cbs);
+ install_node(&mgmt_dbg_node);
+ install_element(ENABLE_NODE, &debug_mgmt_client_fe_cmd);
+ install_element(CONFIG_NODE, &debug_mgmt_client_fe_cmd);
+}
- session = (struct mgmt_fe_client_session *)session_id;
- if (!session || session->client_ctx != client_ctx)
- return MGMTD_INVALID_PARAM;
+uint mgmt_fe_client_session_count(struct mgmt_fe_client *client)
+{
+ return mgmt_sessions_count(&client->sessions);
+}
- if (mgmt_fe_send_getcfg_req(client_ctx, session, req_id, ds_id,
- data_req, num_reqs)
- != 0)
- return MGMTD_INTERNAL_ERROR;
+bool mgmt_fe_client_current_msg_short_circuit(struct mgmt_fe_client *client)
+{
+ return client->client.conn.is_short_circuit;
+}
- return MGMTD_SUCCESS;
+const char *mgmt_fe_client_name(struct mgmt_fe_client *client)
+{
+ return client->name;
}
/*
- * Send GET_DATA_REQ to MGMTD for one or more config data item(s).
+ * Create a new Session for a Frontend Client connection.
*/
-enum mgmt_result mgmt_fe_get_data(uintptr_t lib_hndl, uintptr_t session_id,
- uint64_t req_id, Mgmtd__DatastoreId ds_id,
- Mgmtd__YangGetDataReq * data_req[],
- int num_reqs)
+enum mgmt_result mgmt_fe_create_client_session(struct mgmt_fe_client *client,
+ uint64_t client_id,
+ uintptr_t user_ctx)
{
- struct mgmt_fe_client_ctx *client_ctx;
struct mgmt_fe_client_session *session;
- client_ctx = (struct mgmt_fe_client_ctx *)lib_hndl;
- if (!client_ctx)
- return MGMTD_INVALID_PARAM;
+ session = XCALLOC(MTYPE_MGMTD_FE_SESSION,
+ sizeof(struct mgmt_fe_client_session));
+ assert(session);
+ session->user_ctx = user_ctx;
+ session->client_id = client_id;
+ session->client = client;
+ session->session_id = 0;
- session = (struct mgmt_fe_client_session *)session_id;
- if (!session || session->client_ctx != client_ctx)
- return MGMTD_INVALID_PARAM;
+ mgmt_sessions_add_tail(&client->sessions, session);
- if (mgmt_fe_send_getdata_req(client_ctx, session, req_id, ds_id,
- data_req, num_reqs)
- != 0)
+ if (mgmt_fe_send_session_req(client, session, true) != 0) {
+ XFREE(MTYPE_MGMTD_FE_SESSION, session);
return MGMTD_INTERNAL_ERROR;
+ }
return MGMTD_SUCCESS;
}
/*
- * Send NOTIFY_REGISTER_REQ to MGMTD daemon.
+ * Delete an existing Session for a Frontend Client connection.
*/
-enum mgmt_result
-mgmt_fe_register_yang_notify(uintptr_t lib_hndl, uintptr_t session_id,
- uint64_t req_id, Mgmtd__DatastoreId ds_id,
- bool register_req,
- Mgmtd__YangDataXPath * data_req[],
- int num_reqs)
+enum mgmt_result mgmt_fe_destroy_client_session(struct mgmt_fe_client *client,
+ uint64_t client_id)
{
- struct mgmt_fe_client_ctx *client_ctx;
struct mgmt_fe_client_session *session;
- client_ctx = (struct mgmt_fe_client_ctx *)lib_hndl;
- if (!client_ctx)
+ session = mgmt_fe_find_session_by_client_id(client, client_id);
+ if (!session || session->client != client)
return MGMTD_INVALID_PARAM;
- session = (struct mgmt_fe_client_session *)session_id;
- if (!session || session->client_ctx != client_ctx)
- return MGMTD_INVALID_PARAM;
+ if (session->session_id &&
+ mgmt_fe_send_session_req(client, session, false) != 0)
+ log_err_fe_client("Failed to send session destroy request for the session-id %" PRIu64,
+ session->session_id);
- if (mgmt_fe_send_regnotify_req(client_ctx, session, req_id, ds_id,
- register_req, data_req, num_reqs)
- != 0)
- return MGMTD_INTERNAL_ERROR;
+ mgmt_sessions_del(&client->sessions, session);
+ XFREE(MTYPE_MGMTD_FE_SESSION, session);
return MGMTD_SUCCESS;
}
@@ -1052,24 +845,21 @@ mgmt_fe_register_yang_notify(uintptr_t lib_hndl, uintptr_t session_id,
/*
* Destroy library and cleanup everything.
*/
-void mgmt_fe_client_lib_destroy(uintptr_t lib_hndl)
+void mgmt_fe_client_destroy(struct mgmt_fe_client *client)
{
- struct mgmt_fe_client_ctx *client_ctx;
+ struct mgmt_fe_client_session *session;
+
+ assert(client == __fe_client);
- client_ctx = (struct mgmt_fe_client_ctx *)lib_hndl;
- assert(client_ctx);
+ debug_fe_client("Destroying MGMTD Frontend Client '%s'", client->name);
- MGMTD_FE_CLIENT_DBG("Destroying MGMTD Frontend Client '%s'",
- client_ctx->client_params.name);
+ FOREACH_SESSION_IN_LIST (client, session)
+ mgmt_fe_destroy_client_session(client, session->client_id);
- mgmt_fe_server_disconnect(client_ctx, false);
+ msg_client_cleanup(&client->client);
- mgmt_fe_destroy_client_sessions(lib_hndl);
+ XFREE(MTYPE_MGMTD_FE_CLIENT_NAME, client->name);
+ XFREE(MTYPE_MGMTD_FE_CLIENT, client);
- EVENT_OFF(client_ctx->conn_retry_tmr);
- EVENT_OFF(client_ctx->conn_read_ev);
- EVENT_OFF(client_ctx->conn_write_ev);
- EVENT_OFF(client_ctx->conn_writes_on);
- EVENT_OFF(client_ctx->msg_proc_ev);
- mgmt_msg_destroy(&client_ctx->mstate);
+ __fe_client = NULL;
}
diff --git a/lib/mgmt_fe_client.h b/lib/mgmt_fe_client.h
index aa3371f03c..eee4594e17 100644
--- a/lib/mgmt_fe_client.h
+++ b/lib/mgmt_fe_client.h
@@ -14,7 +14,8 @@ extern "C" {
#include "mgmt_pb.h"
#include "frrevent.h"
-#include "mgmtd/mgmt_defines.h"
+#include "mgmt_defines.h"
+#include "mgmt_msg_native.h"
/***************************************************************
* Macros
@@ -25,22 +26,11 @@ extern "C" {
* connections.
*/
-#define MGMTD_FE_CLIENT_ERROR_STRING_MAX_LEN 32
-
-#define MGMTD_FE_DEFAULT_CONN_RETRY_INTVL_SEC 5
-
#define MGMTD_FE_MSG_PROC_DELAY_USEC 10
-#define MGMTD_FE_MAX_NUM_MSG_PROC 500
-#define MGMTD_FE_MSG_WRITE_DELAY_MSEC 1
+#define MGMTD_FE_MAX_NUM_MSG_PROC 500
#define MGMTD_FE_MAX_NUM_MSG_WRITE 100
-
-#define GMGD_FE_MAX_NUM_REQ_ITEMS 64
-
-#define MGMTD_FE_MSG_MAX_LEN 9000
-
-#define MGMTD_SOCKET_FE_SEND_BUF_SIZE 65535
-#define MGMTD_SOCKET_FE_RECV_BUF_SIZE MGMTD_SOCKET_FE_SEND_BUF_SIZE
+#define MGMTD_FE_MAX_MSG_LEN (64 * 1024)
/***************************************************************
* Data-structures
@@ -56,6 +46,9 @@ extern "C" {
#define MGMTD_DS_OPERATIONAL MGMTD__DATASTORE_ID__OPERATIONAL_DS
#define MGMTD_DS_MAX_ID MGMTD_DS_OPERATIONAL + 1
+struct mgmt_fe_client;
+
+
/*
* All the client specific information this library needs to
* initialize itself, setup connection with MGMTD FrontEnd interface
@@ -66,58 +59,88 @@ extern "C" {
* to initialize the library (See mgmt_fe_client_lib_init for
* more details).
*/
-struct mgmt_fe_client_params {
- char name[MGMTD_CLIENT_NAME_MAX_LEN];
- uintptr_t user_data;
- unsigned long conn_retry_intvl_sec;
-
- void (*client_connect_notify)(uintptr_t lib_hndl,
- uintptr_t user_data,
- bool connected);
-
- void (*client_session_notify)(uintptr_t lib_hndl,
- uintptr_t user_data,
- uint64_t client_id,
+struct mgmt_fe_client_cbs {
+ void (*client_connect_notify)(struct mgmt_fe_client *client,
+ uintptr_t user_data, bool connected);
+
+ void (*client_session_notify)(struct mgmt_fe_client *client,
+ uintptr_t user_data, uint64_t client_id,
bool create, bool success,
uintptr_t session_id,
- uintptr_t user_session_ctx);
+ uintptr_t user_session_client);
- void (*lock_ds_notify)(uintptr_t lib_hndl, uintptr_t user_data,
- uint64_t client_id, uintptr_t session_id,
- uintptr_t user_session_ctx, uint64_t req_id,
+ void (*lock_ds_notify)(struct mgmt_fe_client *client,
+ uintptr_t user_data, uint64_t client_id,
+ uintptr_t session_id,
+ uintptr_t user_session_client, uint64_t req_id,
bool lock_ds, bool success,
Mgmtd__DatastoreId ds_id, char *errmsg_if_any);
- void (*set_config_notify)(uintptr_t lib_hndl, uintptr_t user_data,
- uint64_t client_id, uintptr_t session_id,
- uintptr_t user_session_ctx, uint64_t req_id,
- bool success, Mgmtd__DatastoreId ds_id,
+ void (*set_config_notify)(struct mgmt_fe_client *client,
+ uintptr_t user_data, uint64_t client_id,
+ uintptr_t session_id,
+ uintptr_t user_session_client,
+ uint64_t req_id, bool success,
+ Mgmtd__DatastoreId ds_id, bool implcit_commit,
char *errmsg_if_any);
- void (*commit_config_notify)(
- uintptr_t lib_hndl, uintptr_t user_data, uint64_t client_id,
- uintptr_t session_id, uintptr_t user_session_ctx,
- uint64_t req_id, bool success, Mgmtd__DatastoreId src_ds_id,
- Mgmtd__DatastoreId dst_ds_id, bool validate_only,
- char *errmsg_if_any);
-
- enum mgmt_result (*get_data_notify)(
- uintptr_t lib_hndl, uintptr_t user_data, uint64_t client_id,
- uintptr_t session_id, uintptr_t user_session_ctx,
- uint64_t req_id, bool success, Mgmtd__DatastoreId ds_id,
- Mgmtd__YangData **yang_data, size_t num_data, int next_key,
- char *errmsg_if_any);
-
- enum mgmt_result (*data_notify)(
- uint64_t client_id, uint64_t session_id, uintptr_t user_data,
- uint64_t req_id, Mgmtd__DatastoreId ds_id,
- Mgmtd__YangData **yang_data, size_t num_data);
+ void (*commit_config_notify)(struct mgmt_fe_client *client,
+ uintptr_t user_data, uint64_t client_id,
+ uintptr_t session_id,
+ uintptr_t user_session_client,
+ uint64_t req_id, bool success,
+ Mgmtd__DatastoreId src_ds_id,
+ Mgmtd__DatastoreId dst_ds_id,
+ bool validate_only, char *errmsg_if_any);
+
+ int (*get_data_notify)(struct mgmt_fe_client *client,
+ uintptr_t user_data, uint64_t client_id,
+ uintptr_t session_id,
+ uintptr_t user_session_client, uint64_t req_id,
+ bool success, Mgmtd__DatastoreId ds_id,
+ Mgmtd__YangData **yang_data, size_t num_data,
+ int next_key, char *errmsg_if_any);
+
+ int (*data_notify)(uint64_t client_id, uint64_t session_id,
+ uintptr_t user_data, uint64_t req_id,
+ Mgmtd__DatastoreId ds_id,
+ Mgmtd__YangData **yang_data, size_t num_data);
+
+ /* Called when get-tree result is returned */
+ int (*get_tree_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, Mgmtd__DatastoreId ds_id,
+ LYD_FORMAT result_type, void *result, size_t len,
+ int partial_error);
+
+ /* Called with asynchronous notifications from backends */
+ int (*async_notification)(struct mgmt_fe_client *client,
+ uintptr_t user_data, uint64_t client_id,
+ uint64_t session_id, uintptr_t session_ctx,
+ const char *result);
+
+ /* Called when new native error is returned */
+ int (*error_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, int error,
+ const char *errstr);
};
+extern struct debug mgmt_dbg_fe_client;
+
/***************************************************************
* API prototypes
***************************************************************/
+#define debug_fe_client(fmt, ...) \
+ DEBUGD(&mgmt_dbg_fe_client, "FE-CLIENT: %s: " fmt, __func__, \
+ ##__VA_ARGS__)
+#define log_err_fe_client(fmt, ...) \
+ zlog_err("FE-CLIENT: %s: ERROR: " fmt, __func__, ##__VA_ARGS__)
+#define debug_check_fe_client() \
+ DEBUG_MODE_CHECK(&mgmt_dbg_fe_client, DEBUG_MODE_ALL)
+
/*
* Initialize library and try connecting with MGMTD FrontEnd interface.
*
@@ -128,10 +151,25 @@ struct mgmt_fe_client_params {
* Thread master.
*
* Returns:
- * Frontend client lib handler (nothing but address of mgmt_fe_client_ctx)
+ * Frontend client lib handler (nothing but address of mgmt_fe_client)
+ */
+extern struct mgmt_fe_client *
+mgmt_fe_client_create(const char *client_name, struct mgmt_fe_client_cbs *cbs,
+ uintptr_t user_data, struct event_loop *event_loop);
+
+/*
+ * Initialize library vty (adds debug support).
+ *
+ * This call should be added to your component when enabling other vty
+ * code to enable mgmtd client debugs. When adding, one needs to also
+ * add a their component in `xref2vtysh.py` as well.
+ */
+extern void mgmt_fe_client_lib_vty_init(void);
+
+/*
+ * Print enabled debugging commands.
*/
-extern uintptr_t mgmt_fe_client_lib_init(struct mgmt_fe_client_params *params,
- struct event_loop *master_thread);
+extern void mgmt_debug_fe_client_show_debug(struct vty *vty);
/*
* Create a new Session for a Frontend Client connection.
@@ -142,15 +180,15 @@ extern uintptr_t mgmt_fe_client_lib_init(struct mgmt_fe_client_params *params,
* client_id
* Unique identifier of client.
*
- * user_ctx
+ * user_client
* Client context.
*
* Returns:
* MGMTD_SUCCESS on success, MGMTD_* otherwise.
*/
-extern enum mgmt_result mgmt_fe_create_client_session(uintptr_t lib_hndl,
- uint64_t client_id,
- uintptr_t user_ctx);
+extern enum mgmt_result
+mgmt_fe_create_client_session(struct mgmt_fe_client *client, uint64_t client_id,
+ uintptr_t user_client);
/*
* Delete an existing Session for a Frontend Client connection.
@@ -162,10 +200,11 @@ extern enum mgmt_result mgmt_fe_create_client_session(uintptr_t lib_hndl,
* Unique identifier of client.
*
* Returns:
- * MGMTD_SUCCESS on success, MGMTD_* otherwise.
+ * 0 on success, otherwise msg_conn_send_msg() return values.
*/
-extern enum mgmt_result mgmt_fe_destroy_client_session(uintptr_t lib_hndl,
- uint64_t client_id);
+extern enum mgmt_result
+mgmt_fe_destroy_client_session(struct mgmt_fe_client *client,
+ uint64_t client_id);
/*
* Send UN/LOCK_DS_REQ to MGMTD for a specific Datastore DS.
@@ -186,11 +225,12 @@ extern enum mgmt_result mgmt_fe_destroy_client_session(uintptr_t lib_hndl,
* TRUE for lock request, FALSE for unlock request.
*
* Returns:
- * MGMTD_SUCCESS on success, MGMTD_* otherwise.
+ * 0 on success, otherwise msg_conn_send_msg() return values.
*/
-extern enum mgmt_result
-mgmt_fe_lock_ds(uintptr_t lib_hndl, uintptr_t session_id, uint64_t req_id,
- Mgmtd__DatastoreId ds_id, bool lock_ds);
+extern int mgmt_fe_send_lockds_req(struct mgmt_fe_client *client,
+ uint64_t session_id, uint64_t req_id,
+ Mgmtd__DatastoreId ds_id, bool lock_ds,
+ bool scok);
/*
* Send SET_CONFIG_REQ to MGMTD for one or more config data(s).
@@ -220,13 +260,15 @@ mgmt_fe_lock_ds(uintptr_t lib_hndl, uintptr_t session_id, uint64_t req_id,
* Destination Datastore ID where data needs to be set.
*
* Returns:
- * MGMTD_SUCCESS on success, MGMTD_* otherwise.
+ * 0 on success, otherwise msg_conn_send_msg() return values.
*/
-extern enum mgmt_result
-mgmt_fe_set_config_data(uintptr_t lib_hndl, uintptr_t session_id,
- uint64_t req_id, Mgmtd__DatastoreId ds_id,
- Mgmtd__YangCfgDataReq **config_req, int num_req,
- bool implicit_commit, Mgmtd__DatastoreId dst_ds_id);
+
+extern int mgmt_fe_send_setcfg_req(struct mgmt_fe_client *client,
+ uint64_t session_id, uint64_t req_id,
+ Mgmtd__DatastoreId ds_id,
+ Mgmtd__YangCfgDataReq **config_req,
+ int num_req, bool implicit_commit,
+ Mgmtd__DatastoreId dst_ds_id);
/*
* Send SET_COMMMIT_REQ to MGMTD for one or more config data(s).
@@ -253,16 +295,19 @@ mgmt_fe_set_config_data(uintptr_t lib_hndl, uintptr_t session_id,
* TRUE if need to restore Src DS back to Dest DS, FALSE otherwise.
*
* Returns:
- * MGMTD_SUCCESS on success, MGMTD_* otherwise.
+ * 0 on success, otherwise msg_conn_send_msg() return values.
*/
-extern enum mgmt_result
-mgmt_fe_commit_config_data(uintptr_t lib_hndl, uintptr_t session_id,
- uint64_t req_id, Mgmtd__DatastoreId src_ds_id,
- Mgmtd__DatastoreId dst_ds_id, bool validate_only,
- bool abort);
+extern int mgmt_fe_send_commitcfg_req(struct mgmt_fe_client *client,
+ uint64_t session_id, uint64_t req_id,
+ Mgmtd__DatastoreId src_ds_id,
+ Mgmtd__DatastoreId dst_ds_id,
+ bool validate_only, bool abort);
/*
- * Send GET_CONFIG_REQ to MGMTD for one or more config data item(s).
+ * Send GET_REQ to MGMTD for one or more config data item(s).
+ *
+ * If is_config is true gets config from the MGMTD datastore, otherwise
+ * operational state is queried from the backend clients.
*
* lib_hndl
* Client library handler.
@@ -270,6 +315,9 @@ mgmt_fe_commit_config_data(uintptr_t lib_hndl, uintptr_t session_id,
* session_id
* Client session ID.
*
+ * is_config
+ * True if get-config else get-data.
+ *
* req_id
* Client request ID.
*
@@ -277,29 +325,19 @@ mgmt_fe_commit_config_data(uintptr_t lib_hndl, uintptr_t session_id,
* Datastore ID (Running/Candidate)
*
* data_req
- * Get config requested.
+ * Get xpaths requested.
*
* num_req
- * Number of get config requests.
+ * Number of get xpath requests.
*
* Returns:
- * MGMTD_SUCCESS on success, MGMTD_* otherwise.
+ * 0 on success, otherwise msg_conn_send_msg() return values.
*/
-extern enum mgmt_result
-mgmt_fe_get_config_data(uintptr_t lib_hndl, uintptr_t session_id,
- uint64_t req_id, Mgmtd__DatastoreId ds_id,
- Mgmtd__YangGetDataReq **data_req, int num_reqs);
+extern int mgmt_fe_send_get_req(struct mgmt_fe_client *client,
+ uint64_t session_id, uint64_t req_id,
+ bool is_config, Mgmtd__DatastoreId ds_id,
+ Mgmtd__YangGetDataReq **data_req, int num_reqs);
-/*
- * Send GET_DATA_REQ to MGMTD for one or more data item(s).
- *
- * Similar to get config request but supports getting data
- * from operational ds aka backend clients directly.
- */
-extern enum mgmt_result
-mgmt_fe_get_data(uintptr_t lib_hndl, uintptr_t session_id, uint64_t req_id,
- Mgmtd__DatastoreId ds_id, Mgmtd__YangGetDataReq **data_req,
- int num_reqs);
/*
* Send NOTIFY_REGISTER_REQ to MGMTD daemon.
@@ -326,18 +364,77 @@ mgmt_fe_get_data(uintptr_t lib_hndl, uintptr_t session_id, uint64_t req_id,
* Number of data requests.
*
* Returns:
- * MGMTD_SUCCESS on success, MGMTD_* otherwise.
+ * 0 on success, otherwise msg_conn_send_msg() return values.
*/
-extern enum mgmt_result
-mgmt_fe_register_yang_notify(uintptr_t lib_hndl, uintptr_t session_id,
- uint64_t req_id, Mgmtd__DatastoreId ds_id,
- bool register_req,
- Mgmtd__YangDataXPath **data_req, int num_reqs);
+extern int mgmt_fe_send_regnotify_req(struct mgmt_fe_client *client,
+ uint64_t session_id, uint64_t req_id,
+ Mgmtd__DatastoreId ds_id,
+ bool register_req,
+ Mgmtd__YangDataXPath **data_req,
+ int num_reqs);
+
+/*
+ * Send GET-DATA to MGMTD daemon.
+ *
+ * client
+ * Client object.
+ *
+ * session_id
+ * Client session ID.
+ *
+ * req_id
+ * Client request ID.
+ *
+ * datastore
+ * Datastore for getting data.
+ *
+ * result_type
+ * The LYD_FORMAT of the result.
+ *
+ * flags
+ * Flags to control the behavior of the request.
+ *
+ * defaults
+ * Options to control the reporting of default values.
+ *
+ * xpath
+ * the xpath to get.
+ *
+ * Returns:
+ * 0 on success, otherwise msg_conn_send_msg() return values.
+ */
+extern int mgmt_fe_send_get_data_req(struct mgmt_fe_client *client,
+ uint64_t session_id, uint64_t req_id,
+ uint8_t datastore, LYD_FORMAT result_type,
+ uint8_t flags, uint8_t defaults,
+ const char *xpath);
/*
* Destroy library and cleanup everything.
*/
-extern void mgmt_fe_client_lib_destroy(uintptr_t lib_hndl);
+extern void mgmt_fe_client_destroy(struct mgmt_fe_client *client);
+
+/*
+ * Get count of open sessions.
+ */
+extern uint mgmt_fe_client_session_count(struct mgmt_fe_client *client);
+
+/*
+ * True if the current handled message is being short-circuited
+ */
+extern bool
+mgmt_fe_client_current_msg_short_circuit(struct mgmt_fe_client *client);
+
+/**
+ * Get the name of the client
+ *
+ * Args:
+ * The client object.
+ *
+ * Return:
+ * The name of the client.
+ */
+extern const char *mgmt_fe_client_name(struct mgmt_fe_client *client);
#ifdef __cplusplus
}
diff --git a/lib/mgmt_msg.c b/lib/mgmt_msg.c
index 3f55f82024..aff9af7e84 100644
--- a/lib/mgmt_msg.c
+++ b/lib/mgmt_msg.c
@@ -7,11 +7,15 @@
* Copyright (c) 2023, LabN Consulting, L.L.C.
*/
#include <zebra.h>
+#include <sys/stat.h>
+
+#include "debug.h"
#include "network.h"
#include "sockopt.h"
#include "stream.h"
#include "frrevent.h"
#include "mgmt_msg.h"
+#include "mgmt_msg_native.h"
#define MGMT_MSG_DBG(dbgtag, fmt, ...) \
@@ -22,7 +26,9 @@
} while (0)
#define MGMT_MSG_ERR(ms, fmt, ...) \
- zlog_err("%s: %s: " fmt, ms->idtag, __func__, ##__VA_ARGS__)
+ zlog_err("%s: %s: " fmt, (ms)->idtag, __func__, ##__VA_ARGS__)
+
+DEFINE_MTYPE(LIB, MSG_CONN, "msg connection state");
/**
* Read data from a socket into streams containing 1 or more full msgs headed by
@@ -56,11 +62,12 @@ enum mgmt_msg_rsched mgmt_msg_read(struct mgmt_msg_state *ms, int fd,
*/
while (avail > sizeof(struct mgmt_msg_hdr)) {
n = stream_read_try(ms->ins, fd, avail);
- MGMT_MSG_DBG(dbgtag, "got %zd bytes", n);
/* -2 is normal nothing read, and to retry */
- if (n == -2)
+ if (n == -2) {
+ MGMT_MSG_DBG(dbgtag, "nothing more to read");
break;
+ }
if (n <= 0) {
if (n == 0)
MGMT_MSG_ERR(ms, "got EOF/disconnect");
@@ -70,6 +77,7 @@ enum mgmt_msg_rsched mgmt_msg_read(struct mgmt_msg_state *ms, int fd,
safe_strerror(errno));
return MSR_DISCONNECT;
}
+ MGMT_MSG_DBG(dbgtag, "read %zd bytes", n);
ms->nrxb += n;
avail -= n;
}
@@ -79,9 +87,9 @@ enum mgmt_msg_rsched mgmt_msg_read(struct mgmt_msg_state *ms, int fd,
*/
assert(stream_get_getp(ms->ins) == 0);
left = stream_get_endp(ms->ins);
- while (left > (long)sizeof(struct mgmt_msg_hdr)) {
+ while (left > (ssize_t)sizeof(struct mgmt_msg_hdr)) {
mhdr = (struct mgmt_msg_hdr *)(STREAM_DATA(ms->ins) + total);
- if (mhdr->marker != MGMT_MSG_MARKER) {
+ if (!MGMT_MSG_IS_MARKER(mhdr->marker)) {
MGMT_MSG_DBG(dbgtag, "recv corrupt buffer, disconnect");
return MSR_DISCONNECT;
}
@@ -94,8 +102,30 @@ enum mgmt_msg_rsched mgmt_msg_read(struct mgmt_msg_state *ms, int fd,
mcount++;
}
- if (!mcount)
+ if (!mcount) {
+ /* Didn't manage to read a full message */
+ if (mhdr && avail == 0) {
+ struct stream *news;
+ /*
+ * Message was longer than what was left and we have no
+ * available space to read more in. B/c mcount == 0 the
+ * message starts at the beginning of the stream so
+ * therefor the stream is too small to fit the message..
+ * Resize the stream to fit.
+ */
+ if (mhdr->len > MGMT_MSG_MAX_MSG_ALLOC_LEN) {
+ MGMT_MSG_ERR(ms, "corrupt msg len rcvd %u",
+ mhdr->len);
+ return MSR_DISCONNECT;
+ }
+ news = stream_new(mhdr->len);
+ stream_put(news, mhdr, left);
+ stream_set_endp(news, left);
+ stream_free(ms->ins);
+ ms->ins = news;
+ }
return MSR_SCHED_STREAM;
+ }
/*
* We have read at least one message into the stream, queue it up.
@@ -103,7 +133,11 @@ enum mgmt_msg_rsched mgmt_msg_read(struct mgmt_msg_state *ms, int fd,
mhdr = (struct mgmt_msg_hdr *)(STREAM_DATA(ms->ins) + total);
stream_set_endp(ms->ins, total);
stream_fifo_push(&ms->inq, ms->ins);
- ms->ins = stream_new(ms->max_msg_sz);
+ if (left < (ssize_t)sizeof(struct mgmt_msg_hdr))
+ ms->ins = stream_new(ms->max_msg_sz);
+ else
+ /* handle case where message is greater than max */
+ ms->ins = stream_new(MAX(ms->max_msg_sz, mhdr->len));
if (left) {
stream_put(ms->ins, mhdr, left);
stream_set_endp(ms->ins, left);
@@ -127,8 +161,8 @@ enum mgmt_msg_rsched mgmt_msg_read(struct mgmt_msg_state *ms, int fd,
* true if more to process (so reschedule) else false
*/
bool mgmt_msg_procbufs(struct mgmt_msg_state *ms,
- void (*handle_msg)(void *user, uint8_t *msg,
- size_t msglen),
+ void (*handle_msg)(uint8_t version, uint8_t *msg,
+ size_t msglen, void *user),
void *user, bool debug)
{
const char *dbgtag = debug ? ms->idtag : NULL;
@@ -153,11 +187,13 @@ bool mgmt_msg_procbufs(struct mgmt_msg_state *ms,
left -= mhdr->len, data += mhdr->len) {
mhdr = (struct mgmt_msg_hdr *)data;
- assert(mhdr->marker == MGMT_MSG_MARKER);
+ assert(MGMT_MSG_IS_MARKER(mhdr->marker));
assert(left >= mhdr->len);
- handle_msg(user, (uint8_t *)(mhdr + 1),
- mhdr->len - sizeof(struct mgmt_msg_hdr));
+ handle_msg(MGMT_MSG_MARKER_VERSION(mhdr->marker),
+ (uint8_t *)(mhdr + 1),
+ mhdr->len - sizeof(struct mgmt_msg_hdr),
+ user);
ms->nrxm++;
nproc++;
}
@@ -173,7 +209,7 @@ bool mgmt_msg_procbufs(struct mgmt_msg_state *ms,
}
/**
- * Write data from a onto the socket, using streams that have been queued for
+ * Write data onto the socket, using streams that have been queued for
* sending by mgmt_msg_send_msg. This function should be reschedulable.
*
* Args:
@@ -251,7 +287,7 @@ enum mgmt_msg_wsched mgmt_msg_write(struct mgmt_msg_state *ms, int fd,
dbgtag,
"reached %zu buffer writes, pausing with %zu streams left",
ms->max_write_buf, ms->outq.count);
- return MSW_SCHED_WRITES_OFF;
+ return MSW_SCHED_STREAM;
}
MGMT_MSG_DBG(dbgtag, "flushed all streams from output q");
return MSW_SCHED_NONE;
@@ -264,15 +300,19 @@ enum mgmt_msg_wsched mgmt_msg_write(struct mgmt_msg_state *ms, int fd,
*
* Args:
* ms: mgmt_msg_state for this process.
- * fd: socket/file to read data from.
+ * version: version of this message, will be given to receiving side.
+ * msg: the message to be sent.
+ * len: the length of the message.
+ * packf: a function to pack the message.
* debug: true to enable debug logging.
*
* Returns:
* 0 on success, otherwise -1 on failure. The only failure mode is if a
* the message exceeds the maximum message size configured on init.
*/
-int mgmt_msg_send_msg(struct mgmt_msg_state *ms, void *msg, size_t len,
- mgmt_msg_packf packf, bool debug)
+int mgmt_msg_send_msg(struct mgmt_msg_state *ms, uint8_t version, void *msg,
+ size_t len, size_t (*packf)(void *msg, void *buf),
+ bool debug)
{
const char *dbgtag = debug ? ms->idtag : NULL;
struct mgmt_msg_hdr *mhdr;
@@ -281,23 +321,26 @@ int mgmt_msg_send_msg(struct mgmt_msg_state *ms, void *msg, size_t len,
size_t endp, n;
size_t mlen = len + sizeof(*mhdr);
- if (mlen > ms->max_msg_sz) {
- MGMT_MSG_ERR(ms, "Message %zu > max size %zu, dropping", mlen,
- ms->max_msg_sz);
- return -1;
- }
+ if (mlen > ms->max_msg_sz)
+ MGMT_MSG_DBG(dbgtag, "Sending large msg size %zu > max size %zu",
+ mlen, ms->max_msg_sz);
if (!ms->outs) {
- MGMT_MSG_DBG(dbgtag, "creating new stream for msg len %zu",
- len);
- ms->outs = stream_new(ms->max_msg_sz);
+ MGMT_MSG_DBG(dbgtag, "creating new stream for msg len %zu", mlen);
+ ms->outs = stream_new(MAX(ms->max_msg_sz, mlen));
+ } else if (mlen > ms->max_msg_sz && ms->outs->endp == 0) {
+ /* msg is larger than stream max size get a fit-to-size stream */
+ MGMT_MSG_DBG(dbgtag,
+ "replacing old stream with fit-to-size for msg len %zu",
+ mlen);
+ stream_free(ms->outs);
+ ms->outs = stream_new(mlen);
} else if (STREAM_WRITEABLE(ms->outs) < mlen) {
- MGMT_MSG_DBG(
- dbgtag,
- "enq existing stream len %zu and creating new stream for msg len %zu",
- STREAM_WRITEABLE(ms->outs), mlen);
+ MGMT_MSG_DBG(dbgtag,
+ "enq existing stream len %zu and creating new stream for msg len %zu",
+ STREAM_WRITEABLE(ms->outs), mlen);
stream_fifo_push(&ms->outq, ms->outs);
- ms->outs = stream_new(ms->max_msg_sz);
+ ms->outs = stream_new(MAX(ms->max_msg_sz, mlen));
} else {
MGMT_MSG_DBG(
dbgtag,
@@ -306,14 +349,29 @@ int mgmt_msg_send_msg(struct mgmt_msg_state *ms, void *msg, size_t len,
}
s = ms->outs;
+ if (dbgtag && version == MGMT_MSG_VERSION_NATIVE) {
+ struct mgmt_msg_header *native_msg = msg;
+
+ MGMT_MSG_DBG(
+ dbgtag,
+ "Sending native msg sess/txn-id %"PRIu64" req-id %"PRIu64" code %u",
+ native_msg->refer_id, native_msg->req_id, native_msg->code);
+
+ }
+
/* We have a stream with space, pack the message into it. */
mhdr = (struct mgmt_msg_hdr *)(STREAM_DATA(s) + s->endp);
- mhdr->marker = MGMT_MSG_MARKER;
+ mhdr->marker = MGMT_MSG_MARKER(version);
mhdr->len = mlen;
stream_forward_endp(s, sizeof(*mhdr));
endp = stream_get_endp(s);
dstbuf = STREAM_DATA(s) + endp;
- n = packf(msg, dstbuf);
+ if (packf)
+ n = packf(msg, dstbuf);
+ else {
+ memcpy(dstbuf, msg, len);
+ n = len;
+ }
stream_set_endp(s, endp + n);
ms->ntxm++;
@@ -392,6 +450,7 @@ size_t mgmt_msg_reset_writes(struct mgmt_msg_state *ms)
return nproc;
}
+
void mgmt_msg_init(struct mgmt_msg_state *ms, size_t max_read_buf,
size_t max_write_buf, size_t max_msg_sz, const char *idtag)
{
@@ -410,5 +469,509 @@ void mgmt_msg_destroy(struct mgmt_msg_state *ms)
mgmt_msg_reset_writes(ms);
if (ms->ins)
stream_free(ms->ins);
+ if (ms->outs)
+ stream_free(ms->outs);
free(ms->idtag);
}
+
+/*
+ * Connections
+ */
+
+#define MSG_CONN_DEFAULT_CONN_RETRY_MSEC 250
+#define MSG_CONN_SEND_BUF_SIZE (1u << 16)
+#define MSG_CONN_RECV_BUF_SIZE (1u << 16)
+
+static void msg_client_sched_connect(struct msg_client *client,
+ unsigned long msec);
+
+static void msg_conn_sched_proc_msgs(struct msg_conn *conn);
+static void msg_conn_sched_read(struct msg_conn *conn);
+static void msg_conn_sched_write(struct msg_conn *conn);
+
+static void msg_conn_write(struct event *thread)
+{
+ struct msg_conn *conn = EVENT_ARG(thread);
+ enum mgmt_msg_wsched rv;
+
+ rv = mgmt_msg_write(&conn->mstate, conn->fd, conn->debug);
+ if (rv == MSW_SCHED_STREAM)
+ msg_conn_sched_write(conn);
+ else if (rv == MSW_DISCONNECT)
+ msg_conn_disconnect(conn, conn->is_client);
+ else
+ assert(rv == MSW_SCHED_NONE);
+}
+
+static void msg_conn_read(struct event *thread)
+{
+ struct msg_conn *conn = EVENT_ARG(thread);
+ enum mgmt_msg_rsched rv;
+
+ rv = mgmt_msg_read(&conn->mstate, conn->fd, conn->debug);
+ if (rv == MSR_DISCONNECT) {
+ msg_conn_disconnect(conn, conn->is_client);
+ return;
+ }
+ if (rv == MSR_SCHED_BOTH)
+ msg_conn_sched_proc_msgs(conn);
+ msg_conn_sched_read(conn);
+}
+
+/* collapse this into mgmt_msg_procbufs */
+static void msg_conn_proc_msgs(struct event *thread)
+{
+ struct msg_conn *conn = EVENT_ARG(thread);
+
+ if (mgmt_msg_procbufs(&conn->mstate,
+ (void (*)(uint8_t, uint8_t *, size_t,
+ void *))conn->handle_msg,
+ conn, conn->debug))
+ /* there's more, schedule handling more */
+ msg_conn_sched_proc_msgs(conn);
+}
+
+static void msg_conn_sched_read(struct msg_conn *conn)
+{
+ event_add_read(conn->loop, msg_conn_read, conn, conn->fd,
+ &conn->read_ev);
+}
+
+static void msg_conn_sched_write(struct msg_conn *conn)
+{
+ event_add_write(conn->loop, msg_conn_write, conn, conn->fd,
+ &conn->write_ev);
+}
+
+static void msg_conn_sched_proc_msgs(struct msg_conn *conn)
+{
+ event_add_event(conn->loop, msg_conn_proc_msgs, conn, 0,
+ &conn->proc_msg_ev);
+}
+
+
+void msg_conn_disconnect(struct msg_conn *conn, bool reconnect)
+{
+
+ /* disconnect short-circuit if present */
+ if (conn->remote_conn) {
+ conn->remote_conn->remote_conn = NULL;
+ conn->remote_conn = NULL;
+ }
+
+ if (conn->fd != -1) {
+ close(conn->fd);
+ conn->fd = -1;
+
+ /* Notify client through registered callback (if any) */
+ if (conn->notify_disconnect)
+ (void)(*conn->notify_disconnect)(conn);
+ }
+
+ if (reconnect) {
+ assert(conn->is_client);
+ msg_client_sched_connect(
+ container_of(conn, struct msg_client, conn),
+ MSG_CONN_DEFAULT_CONN_RETRY_MSEC);
+ }
+}
+
+int msg_conn_send_msg(struct msg_conn *conn, uint8_t version, void *msg,
+ size_t mlen, size_t (*packf)(void *, void *),
+ bool short_circuit_ok)
+{
+ const char *dbgtag = conn->debug ? conn->mstate.idtag : NULL;
+
+ if (conn->fd == -1) {
+ MGMT_MSG_ERR(&conn->mstate,
+ "can't send message on closed connection");
+ return -1;
+ }
+
+ /* immediately handle the message if short-circuit is present */
+ if (conn->remote_conn && short_circuit_ok) {
+ uint8_t *buf = msg;
+ size_t n = mlen;
+ bool old;
+
+ if (packf) {
+ buf = XMALLOC(MTYPE_TMP, mlen);
+ n = packf(msg, buf);
+ }
+
+ ++conn->short_circuit_depth;
+ MGMT_MSG_DBG(dbgtag, "SC send: depth %u msg: %p",
+ conn->short_circuit_depth, msg);
+
+ old = conn->remote_conn->is_short_circuit;
+ conn->remote_conn->is_short_circuit = true;
+ conn->remote_conn->handle_msg(version, buf, n,
+ conn->remote_conn);
+ conn->remote_conn->is_short_circuit = old;
+
+ --conn->short_circuit_depth;
+ MGMT_MSG_DBG(dbgtag, "SC return from depth: %u msg: %p",
+ conn->short_circuit_depth, msg);
+
+ if (packf)
+ XFREE(MTYPE_TMP, buf);
+ return 0;
+ }
+
+ int rv = mgmt_msg_send_msg(&conn->mstate, version, msg, mlen, packf,
+ conn->debug);
+
+ msg_conn_sched_write(conn);
+
+ return rv;
+}
+
+void msg_conn_cleanup(struct msg_conn *conn)
+{
+ struct mgmt_msg_state *ms = &conn->mstate;
+
+ /* disconnect short-circuit if present */
+ if (conn->remote_conn) {
+ conn->remote_conn->remote_conn = NULL;
+ conn->remote_conn = NULL;
+ }
+
+ if (conn->fd != -1) {
+ close(conn->fd);
+ conn->fd = -1;
+ }
+
+ EVENT_OFF(conn->read_ev);
+ EVENT_OFF(conn->write_ev);
+ EVENT_OFF(conn->proc_msg_ev);
+
+ mgmt_msg_destroy(ms);
+}
+
+/*
+ * Client Connections
+ */
+
+DECLARE_LIST(msg_server_list, struct msg_server, link);
+
+static struct msg_server_list_head msg_servers;
+
+static void msg_client_connect(struct msg_client *conn);
+
+static void msg_client_connect_timer(struct event *thread)
+{
+ msg_client_connect(EVENT_ARG(thread));
+}
+
+static void msg_client_sched_connect(struct msg_client *client,
+ unsigned long msec)
+{
+ struct msg_conn *conn = &client->conn;
+ const char *dbgtag = conn->debug ? conn->mstate.idtag : NULL;
+
+ MGMT_MSG_DBG(dbgtag, "connection retry in %lu msec", msec);
+ if (msec)
+ event_add_timer_msec(conn->loop, msg_client_connect_timer,
+ client, msec, &client->conn_retry_tmr);
+ else
+ event_add_event(conn->loop, msg_client_connect_timer, client, 0,
+ &client->conn_retry_tmr);
+}
+
+static int msg_client_connect_short_circuit(struct msg_client *client)
+{
+ struct msg_conn *server_conn;
+ struct msg_server *server;
+ const char *dbgtag =
+ client->conn.debug ? client->conn.mstate.idtag : NULL;
+ union sockunion su = {};
+ int sockets[2];
+
+ frr_each (msg_server_list, &msg_servers, server)
+ if (!strcmp(server->sopath, client->sopath))
+ break;
+ if (!server) {
+ MGMT_MSG_DBG(dbgtag,
+ "no short-circuit server available yet for %s",
+ client->sopath);
+ return -1;
+ }
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets)) {
+ MGMT_MSG_ERR(
+ &client->conn.mstate,
+ "socketpair failed trying to short-circuit connection on %s: %s",
+ client->sopath, safe_strerror(errno));
+ return -1;
+ }
+
+ /* client side */
+ client->conn.fd = sockets[0];
+ set_nonblocking(sockets[0]);
+ setsockopt_so_sendbuf(sockets[0], client->conn.mstate.max_write_buf);
+ setsockopt_so_recvbuf(sockets[0], client->conn.mstate.max_read_buf);
+
+ /* server side */
+ memset(&su, 0, sizeof(union sockunion));
+ server_conn = server->create(sockets[1], &su);
+ server_conn->debug = DEBUG_MODE_CHECK(server->debug, DEBUG_MODE_ALL)
+ ? true
+ : false;
+
+ client->conn.remote_conn = server_conn;
+ server_conn->remote_conn = &client->conn;
+
+ MGMT_MSG_DBG(
+ dbgtag,
+ "short-circuit connection on %s server %s:%d to client %s:%d",
+ client->sopath, server_conn->mstate.idtag, server_conn->fd,
+ client->conn.mstate.idtag, client->conn.fd);
+
+ MGMT_MSG_DBG(
+ server_conn->debug ? server_conn->mstate.idtag : NULL,
+ "short-circuit connection on %s client %s:%d to server %s:%d",
+ client->sopath, client->conn.mstate.idtag, client->conn.fd,
+ server_conn->mstate.idtag, server_conn->fd);
+
+ return 0;
+}
+
+
+/* Connect and start reading from the socket */
+static void msg_client_connect(struct msg_client *client)
+{
+ struct msg_conn *conn = &client->conn;
+ const char *dbgtag = conn->debug ? conn->mstate.idtag : NULL;
+
+ if (!client->short_circuit_ok)
+ conn->fd =
+ mgmt_msg_connect(client->sopath, MSG_CONN_SEND_BUF_SIZE,
+ MSG_CONN_RECV_BUF_SIZE, dbgtag);
+ else if (msg_client_connect_short_circuit(client))
+ conn->fd = -1;
+
+ if (conn->fd == -1)
+ /* retry the connection */
+ msg_client_sched_connect(client,
+ MSG_CONN_DEFAULT_CONN_RETRY_MSEC);
+ else if (client->notify_connect && client->notify_connect(client))
+ /* client connect notify failed */
+ msg_conn_disconnect(conn, true);
+ else
+ /* start reading */
+ msg_conn_sched_read(conn);
+}
+
+void msg_client_init(struct msg_client *client, struct event_loop *tm,
+ const char *sopath,
+ int (*notify_connect)(struct msg_client *client),
+ int (*notify_disconnect)(struct msg_conn *client),
+ void (*handle_msg)(uint8_t version, uint8_t *data,
+ size_t len, struct msg_conn *client),
+ size_t max_read_buf, size_t max_write_buf,
+ size_t max_msg_sz, bool short_circuit_ok,
+ const char *idtag, bool debug)
+{
+ struct msg_conn *conn = &client->conn;
+ memset(client, 0, sizeof(*client));
+
+ conn->loop = tm;
+ conn->fd = -1;
+ conn->handle_msg = handle_msg;
+ conn->notify_disconnect = notify_disconnect;
+ conn->is_client = true;
+ conn->debug = debug;
+ client->short_circuit_ok = short_circuit_ok;
+ client->sopath = strdup(sopath);
+ client->notify_connect = notify_connect;
+
+ mgmt_msg_init(&conn->mstate, max_read_buf, max_write_buf, max_msg_sz,
+ idtag);
+
+ /* Start trying to connect to server */
+ msg_client_sched_connect(client, 0);
+}
+
+void msg_client_cleanup(struct msg_client *client)
+{
+ assert(client->conn.is_client);
+
+ EVENT_OFF(client->conn_retry_tmr);
+ free(client->sopath);
+
+ msg_conn_cleanup(&client->conn);
+}
+
+
+/*
+ * Server-side connections
+ */
+
+static void msg_server_accept(struct event *event)
+{
+ struct msg_server *server = EVENT_ARG(event);
+ struct msg_conn *conn;
+ union sockunion su;
+ int fd;
+
+ if (server->fd < 0)
+ return;
+
+ /* We continue hearing server listen socket. */
+ event_add_read(server->loop, msg_server_accept, server, server->fd,
+ &server->listen_ev);
+
+ memset(&su, 0, sizeof(union sockunion));
+
+ /* We can handle IPv4 or IPv6 socket. */
+ fd = sockunion_accept(server->fd, &su);
+ if (fd < 0) {
+ zlog_err("Failed to accept %s client connection: %s",
+ server->idtag, safe_strerror(errno));
+ return;
+ }
+ set_nonblocking(fd);
+ set_cloexec(fd);
+
+ DEBUGD(server->debug, "Accepted new %s connection", server->idtag);
+
+ conn = server->create(fd, &su);
+ if (conn)
+ conn->debug = DEBUG_MODE_CHECK(server->debug, DEBUG_MODE_ALL)
+ ? true
+ : false;
+}
+
+int msg_server_init(struct msg_server *server, const char *sopath,
+ struct event_loop *loop,
+ struct msg_conn *(*create)(int fd, union sockunion *su),
+ const char *idtag, struct debug *debug)
+{
+ int ret;
+ int sock;
+ struct sockaddr_un addr;
+ mode_t old_mask;
+
+ memset(server, 0, sizeof(*server));
+ server->fd = -1;
+
+ sock = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC);
+ if (sock < 0) {
+ zlog_err("Failed to create %s server socket: %s", server->idtag,
+ safe_strerror(errno));
+ goto fail;
+ }
+
+ addr.sun_family = AF_UNIX,
+ strlcpy(addr.sun_path, sopath, sizeof(addr.sun_path));
+ unlink(addr.sun_path);
+ old_mask = umask(0077);
+ ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
+ if (ret < 0) {
+ zlog_err("Failed to bind %s server socket to '%s': %s",
+ server->idtag, addr.sun_path, safe_strerror(errno));
+ umask(old_mask);
+ goto fail;
+ }
+ umask(old_mask);
+
+ ret = listen(sock, MGMTD_MAX_CONN);
+ if (ret < 0) {
+ zlog_err("Failed to listen on %s server socket: %s",
+ server->idtag, safe_strerror(errno));
+ goto fail;
+ }
+
+ server->fd = sock;
+ server->loop = loop;
+ server->sopath = strdup(sopath);
+ server->idtag = strdup(idtag);
+ server->create = create;
+ server->debug = debug;
+
+ msg_server_list_add_head(&msg_servers, server);
+
+ event_add_read(server->loop, msg_server_accept, server, server->fd,
+ &server->listen_ev);
+
+
+ DEBUGD(debug, "Started %s server, listening on %s", idtag, sopath);
+ return 0;
+
+fail:
+ if (sock >= 0)
+ close(sock);
+ server->fd = -1;
+ return -1;
+}
+
+void msg_server_cleanup(struct msg_server *server)
+{
+ DEBUGD(server->debug, "Closing %s server", server->idtag);
+
+ if (server->listen_ev)
+ EVENT_OFF(server->listen_ev);
+
+ msg_server_list_del(&msg_servers, server);
+
+ if (server->fd >= 0)
+ close(server->fd);
+ free((char *)server->sopath);
+ free((char *)server->idtag);
+
+ memset(server, 0, sizeof(*server));
+ server->fd = -1;
+}
+
+/*
+ * Initialize and start reading from the accepted socket
+ *
+ * notify_connect - only called for disconnect i.e., connected == false
+ */
+void msg_conn_accept_init(struct msg_conn *conn, struct event_loop *tm, int fd,
+ int (*notify_disconnect)(struct msg_conn *conn),
+ void (*handle_msg)(uint8_t version, uint8_t *data,
+ size_t len, struct msg_conn *conn),
+ size_t max_read, size_t max_write, size_t max_size,
+ const char *idtag)
+{
+ conn->loop = tm;
+ conn->fd = fd;
+ conn->notify_disconnect = notify_disconnect;
+ conn->handle_msg = handle_msg;
+ conn->is_client = false;
+
+ mgmt_msg_init(&conn->mstate, max_read, max_write, max_size, idtag);
+
+ /* start reading */
+ msg_conn_sched_read(conn);
+
+ /* Make socket non-blocking. */
+ set_nonblocking(conn->fd);
+ setsockopt_so_sendbuf(conn->fd, MSG_CONN_SEND_BUF_SIZE);
+ setsockopt_so_recvbuf(conn->fd, MSG_CONN_RECV_BUF_SIZE);
+}
+
+struct msg_conn *
+msg_server_conn_create(struct event_loop *tm, int fd,
+ int (*notify_disconnect)(struct msg_conn *conn),
+ void (*handle_msg)(uint8_t version, uint8_t *data,
+ size_t len, struct msg_conn *conn),
+ size_t max_read, size_t max_write, size_t max_size,
+ void *user, const char *idtag)
+{
+ struct msg_conn *conn = XMALLOC(MTYPE_MSG_CONN, sizeof(*conn));
+ memset(conn, 0, sizeof(*conn));
+ msg_conn_accept_init(conn, tm, fd, notify_disconnect, handle_msg,
+ max_read, max_write, max_size, idtag);
+ conn->user = user;
+ return conn;
+}
+
+void msg_server_conn_delete(struct msg_conn *conn)
+{
+ if (!conn)
+ return;
+ msg_conn_cleanup(conn);
+ XFREE(MTYPE_MSG_CONN, conn);
+}
diff --git a/lib/mgmt_msg.h b/lib/mgmt_msg.h
index e2dd2d476a..6bdc9a6cfc 100644
--- a/lib/mgmt_msg.h
+++ b/lib/mgmt_msg.h
@@ -7,10 +7,25 @@
#ifndef _MGMT_MSG_H
#define _MGMT_MSG_H
+#include "memory.h"
#include "stream.h"
#include "frrevent.h"
-#define MGMT_MSG_MARKER (0x4D724B21u) /* ASCII - "MrK!"*/
+DECLARE_MTYPE(MSG_CONN);
+
+/*
+ * Messages on the stream start with a marker that encodes a version octet.
+ */
+#define MGMT_MSG_MARKER_PFX (0x23232300u) /* ASCII - "###\ooo"*/
+#define MGMT_MSG_IS_MARKER(x) (((x)&0xFFFFFF00u) == MGMT_MSG_MARKER_PFX)
+#define MGMT_MSG_MARKER(version) (MGMT_MSG_MARKER_PFX | (version))
+#define MGMT_MSG_MARKER_VERSION(x) (0xFF & (x))
+
+#define MGMT_MSG_VERSION_PROTOBUF 0
+#define MGMT_MSG_VERSION_NATIVE 1
+
+/* The absolute maximum message size (16MB) */
+#define MGMT_MSG_MAX_MSG_ALLOC_LEN (16 * 1024 * 1024)
struct mgmt_msg_state {
struct stream *ins;
@@ -41,33 +56,152 @@ enum mgmt_msg_rsched {
enum mgmt_msg_wsched {
MSW_SCHED_NONE, /* no scheduling required */
MSW_SCHED_STREAM, /* schedule writing */
- MSW_SCHED_WRITES_OFF, /* toggle writes off */
MSW_DISCONNECT, /* disconnect and start reconnecting */
};
-static inline uint8_t *msg_payload(struct mgmt_msg_hdr *mhdr)
-{
- return (uint8_t *)(mhdr + 1);
-}
+struct msg_conn;
-typedef size_t (*mgmt_msg_packf)(void *msg, void *data);
extern int mgmt_msg_connect(const char *path, size_t sendbuf, size_t recvbuf,
const char *dbgtag);
-extern void mgmt_msg_destroy(struct mgmt_msg_state *ms);
-extern void mgmt_msg_init(struct mgmt_msg_state *ms, size_t max_read_buf,
- size_t max_write_buf, size_t max_msg_sz,
- const char *idtag);
extern bool mgmt_msg_procbufs(struct mgmt_msg_state *ms,
- void (*handle_msg)(void *user, uint8_t *msg,
- size_t msglen),
+ void (*handle_msg)(uint8_t version, uint8_t *msg,
+ size_t msglen, void *user),
void *user, bool debug);
extern enum mgmt_msg_rsched mgmt_msg_read(struct mgmt_msg_state *ms, int fd,
bool debug);
extern size_t mgmt_msg_reset_writes(struct mgmt_msg_state *ms);
-extern int mgmt_msg_send_msg(struct mgmt_msg_state *ms, void *msg, size_t len,
+extern int mgmt_msg_send_msg(struct mgmt_msg_state *ms, uint8_t version,
+ void *msg, size_t len,
size_t (*packf)(void *msg, void *buf), bool debug);
extern enum mgmt_msg_wsched mgmt_msg_write(struct mgmt_msg_state *ms, int fd,
bool debug);
+extern void mgmt_msg_destroy(struct mgmt_msg_state *state);
+
+extern void mgmt_msg_init(struct mgmt_msg_state *ms, size_t max_read_buf,
+ size_t max_write_buf, size_t max_msg_sz,
+ const char *idtag);
+
+/*
+ * Connections
+ */
+
+struct msg_conn {
+ int fd;
+ struct mgmt_msg_state mstate;
+ struct event_loop *loop;
+ struct event *read_ev;
+ struct event *write_ev;
+ struct event *proc_msg_ev;
+ struct msg_conn *remote_conn;
+ int (*notify_disconnect)(struct msg_conn *conn);
+ void (*handle_msg)(uint8_t version, uint8_t *data, size_t len,
+ struct msg_conn *conn);
+ void *user;
+ uint short_circuit_depth;
+ bool is_short_circuit; /* true when the message being handled is SC */
+ bool is_client;
+ bool debug;
+};
+
+
+/*
+ * `notify_disconnect` is not called when `msg_conn_cleanup` is called for a
+ * msg_conn which is currently connected. The socket is closed but there is no
+ * notification.
+ */
+extern void msg_conn_cleanup(struct msg_conn *conn);
+extern void msg_conn_disconnect(struct msg_conn *conn, bool reconnect);
+extern int msg_conn_send_msg(struct msg_conn *client, uint8_t version,
+ void *msg, size_t mlen,
+ size_t (*packf)(void *, void *),
+ bool short_circuit_ok);
+
+/*
+ * Client-side Connections
+ */
+
+struct msg_client {
+ struct msg_conn conn;
+ struct event *conn_retry_tmr;
+ char *sopath;
+ int (*notify_connect)(struct msg_client *client);
+ bool short_circuit_ok;
+};
+
+/*
+ * `notify_disconnect` is not called when `msg_client_cleanup` is called for a
+ * msg_client which is currently connected. The socket is closed but there is no
+ * notification.
+ */
+extern void msg_client_cleanup(struct msg_client *client);
+
+/*
+ * If `short_circuit_ok` is true, then the client-server connection will use a
+ * socketpair() rather than a unix-domain socket. This must be passed true if
+ * you wish to send messages short-circuit later.
+ *
+ * `notify_disconnect` is not called when the user `msg_client_cleanup` is
+ * called for a client which is currently connected. The socket is closed
+ * but there is no notification.
+ */
+extern void
+msg_client_init(struct msg_client *client, struct event_loop *tm,
+ const char *sopath,
+ int (*notify_connect)(struct msg_client *client),
+ int (*notify_disconnect)(struct msg_conn *client),
+ void (*handle_msg)(uint8_t version, uint8_t *data, size_t len,
+ struct msg_conn *client),
+ size_t max_read_buf, size_t max_write_buf, size_t max_msg_sz,
+ bool short_circuit_ok, const char *idtag, bool debug);
+
+/*
+ * Server-side Connections
+ */
+#define MGMTD_MAX_CONN 32
+
+PREDECL_LIST(msg_server_list);
+
+struct msg_server {
+ int fd;
+ struct msg_server_list_item link;
+ struct event_loop *loop;
+ struct event *listen_ev;
+ const char *sopath;
+ const char *idtag;
+ struct msg_conn *(*create)(int fd, union sockunion *su);
+ struct debug *debug;
+};
+
+extern int msg_server_init(struct msg_server *server, const char *sopath,
+ struct event_loop *loop,
+ struct msg_conn *(*create)(int fd,
+ union sockunion *su),
+ const char *idtag, struct debug *debug);
+extern void msg_server_cleanup(struct msg_server *server);
+
+/*
+ * `notify_disconnect` is not called when the user `msg_conn_cleanup` is
+ * called for a client which is currently connected. The socket is closed
+ * but there is no notification.
+ */
+struct msg_conn *
+msg_server_conn_create(struct event_loop *tm, int fd,
+ int (*notify_disconnect)(struct msg_conn *conn),
+ void (*handle_msg)(uint8_t version, uint8_t *data,
+ size_t len, struct msg_conn *conn),
+ size_t max_read, size_t max_write, size_t max_size,
+ void *user, const char *idtag);
+
+extern void msg_server_conn_delete(struct msg_conn *conn);
+
+extern void
+msg_conn_accept_init(struct msg_conn *conn, struct event_loop *tm, int fd,
+ int (*notify_disconnect)(struct msg_conn *conn),
+ void (*handle_msg)(uint8_t version, uint8_t *data,
+ size_t len, struct msg_conn *conn),
+ size_t max_read, size_t max_write, size_t max_size,
+ const char *idtag);
+
#endif /* _MGMT_MSG_H */
diff --git a/lib/mgmt_msg_native.c b/lib/mgmt_msg_native.c
new file mode 100644
index 0000000000..98b7da45ce
--- /dev/null
+++ b/lib/mgmt_msg_native.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * June 29 2023, Christian Hopps <chopps@labn.net>
+ *
+ * Copyright (c) 2023, LabN Consulting, L.L.C.
+ *
+ */
+#include <zebra.h>
+#include "mgmt_msg_native.h"
+
+DEFINE_MGROUP(MSG_NATIVE, "Native message allocations");
+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");
+
+int vmgmt_msg_native_send_error(struct msg_conn *conn, uint64_t sess_or_txn_id,
+ uint64_t req_id, bool short_circuit_ok,
+ int16_t error, const char *errfmt, va_list ap)
+{
+ struct mgmt_msg_error *msg;
+ char *errstr;
+ ssize_t slen;
+ int ret;
+
+ errstr = darr_vsprintf(errfmt, ap);
+ slen = strlen(errstr);
+
+ msg = mgmt_msg_native_alloc_msg(typeof(*msg), slen + 1,
+ MTYPE_MSG_NATIVE_ERROR);
+ msg->refer_id = sess_or_txn_id;
+ msg->req_id = req_id;
+ msg->code = MGMT_MSG_CODE_ERROR;
+ msg->error = error;
+ strlcpy(msg->errstr, errstr, slen + 1);
+ darr_free(errstr);
+
+ if (conn->debug)
+ zlog_debug("Sending error %d session-id %" PRIu64
+ " req-id %" PRIu64 " scok %d errstr: %s",
+ error, sess_or_txn_id, req_id, short_circuit_ok,
+ msg->errstr);
+
+ ret = mgmt_msg_native_send_msg(conn, msg, short_circuit_ok);
+ mgmt_msg_native_free_msg(msg);
+ return ret;
+}
diff --git a/lib/mgmt_msg_native.h b/lib/mgmt_msg_native.h
new file mode 100644
index 0000000000..53bb81be28
--- /dev/null
+++ b/lib/mgmt_msg_native.h
@@ -0,0 +1,585 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * June 29 2023, Christian Hopps <chopps@labn.net>
+ *
+ * Copyright (c) 2023, LabN Consulting, L.L.C.
+ *
+ */
+
+#ifndef _FRR_MGMT_MSG_NATIVE_H_
+#define _FRR_MGMT_MSG_NATIVE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#elif 0
+}
+#endif
+
+#include <zebra.h>
+#include "compiler.h"
+#include "darr.h"
+#include "memory.h"
+#include "mgmt_msg.h"
+#include "mgmt_defines.h"
+
+#include <stdalign.h>
+
+/*
+ * ==================
+ * Native Message API
+ * ==================
+ *
+ * -----------------------
+ * Defining A New Message:
+ * -----------------------
+ *
+ * 1) Start with `struct mgmt_msg_header` as the first (unnamed) field.
+ *
+ * 2) Add fixed-width fields. Add on natural aligned boundaries (*)
+ *
+ * 3) [Optional] Add a zero-length variable field. Add aligned on a 64-bit
+ * boundary, this is done so that: `value = (HDR + 1)` works.
+ *
+ * 4) Define a new MTYPE for the new message type (see DECLARE_MTYPE below
+ * as well as the paired DEFINE_MTYPE in mgmt_msg_native.c)
+ *
+ * These rules are so the messages may be read from and written directly to
+ * "the wire", easily, using common programming languages (e.g., C, rust, go,
+ * python, ...)
+ *
+ * (*) Natrual aligned boundaries, i.e., uint16_t on 2-byte boundary, uint64_t
+ * on 8-byte boundaries, ...)
+ *
+ * ------------------------------
+ * Allocating New Native Messages
+ * ------------------------------
+ *
+ * For fixed-length and variable length messages one should allocate new
+ * messages with the mgmt_msg_native_alloc_msg() passing in the newly defined
+ * MTYPE. Likewise, to free the message one should use
+ * mgmt_msg_native_free_msg().
+ *
+ * Unknown Variable Length Messages:
+ * ---------------------------------
+ *
+ * If using a zero-length variable length field and the length is not known at
+ * message creation time, you can use the `native` API function
+ * mgmt_msg_native_append() to add data to the end of the message, or if a more
+ * full set of operations are required, the darr_xxxx() API is also available as
+ * in the Advanced section below.
+ *
+ * Notable API Functions:
+ * ---------------------------------
+ *
+ * mgmt_msg_native_alloc_msg() - Allocate a native msg.
+ * mgmt_msg_native_free_msg() - Free a native msg.
+ * mgmt_msg_native_append() - Append data to the end of the msg.
+ * mgmt_msg_native_get_msg_len() - Get the total length of the msg.
+ * mgmt_msg_native_send_msg() - Send the message.
+ *
+ * mgmt_msg_native_xpath_encode() - Encode xpath in xpath, data format message.
+ * mgmt_msg_native_xpath_data_decode() - Decode xpath, data format message.
+ * mgmt_msg_native_xpath_decode() - Get the xpath, from xpath, data format message.
+ * mgmt_msg_native_data_decode() - Get the secondary data from xpath, data message.
+ * mgmt_msg_native_data_len_decode() - Get length of secondary data.
+ *
+ * -------------------------------------
+ * [Advanced Use] Dynamic Array Messages
+ * -------------------------------------
+ *
+ * NOTE: Most users can simply use mgmt_msg_native_append() and skip this
+ * section.
+ *
+ * This section is only important to understand if you wish to utilize the fact
+ * that native messages allocated with mgmt_msg_native_alloc_msg are
+ * actually allocated as uint8_t dynamic arrays (`darr`).
+ *
+ * You can utilize all the darr_xxxx() API to manipulate the variable length
+ * message data in a native message. To do so you simply need to understand that
+ * the native message is actually a `uint8_t *` darr. So, for example, to append
+ * data to the end of a message one could do the following:
+ *
+ * void append_metric_path(struct mgmt_msg_my_msg *msg)
+ * {
+ * msg = (struct mggm_msg_my_msg *)
+ * darr_strcat((uint8_t *)msg, "/metric");
+ *
+ * // ...
+ * }
+ *
+ * NOTE: If reallocs happen the original passed in pointer will be updated;
+ * however, any other pointers into the message will become invalid, and so they
+ * should always be discarded or reinitialized after using any reallocating
+ * darr_xxx() API functions.
+ *
+ * void append_metric_path(struct mgmt_msg_my_msg *msg)
+ * {
+ * char *xpath = msg->xpath; // pointer into message
+ *
+ * darr_in_strcat((uint8_t *)msg, "/metric");
+ * // msg may have been updated to point at new memory
+ *
+ * xpath = NULL; // now invalid
+ * xpath = msg->xpath; // reinitialize
+ * // ...
+ * }
+ *
+ * Rather than worry about this, it's typical when using dynamic arrays to always
+ * work from the main pointer to the dynamic array, rather than caching multiple
+ * pointers into the data. Modern compilers will optimize the code so that it
+ * adds no extra execution cost.
+ *
+ * void append_metric_path(struct mgmt_msg_my_msg *msg)
+ * {
+ * darr_in_strcat((uint8_t *)msg, "/metric");
+ *
+ * // Use `msg->xpath` directly rather creating and using an
+ * // `xpath = msg->xpath` local variable.
+ *
+ * if (strcmp(msg->xpath, "foobar/metric")) {
+ * // ...
+ * }
+ * }
+ *
+ */
+
+DECLARE_MTYPE(MSG_NATIVE_MSG);
+DECLARE_MTYPE(MSG_NATIVE_ERROR);
+DECLARE_MTYPE(MSG_NATIVE_GET_TREE);
+DECLARE_MTYPE(MSG_NATIVE_TREE_DATA);
+DECLARE_MTYPE(MSG_NATIVE_GET_DATA);
+DECLARE_MTYPE(MSG_NATIVE_NOTIFY);
+
+/*
+ * 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
+
+/*
+ * Datastores
+ */
+#define MGMT_MSG_DATASTORE_STARTUP 0
+#define MGMT_MSG_DATASTORE_CANDIDATE 1
+#define MGMT_MSG_DATASTORE_RUNNING 2
+#define MGMT_MSG_DATASTORE_OPERATIONAL 3
+
+/*
+ * Formats
+ */
+#define MGMT_MSG_FORMAT_XML 1
+#define MGMT_MSG_FORMAT_JSON 2
+#define MGMT_MSG_FORMAT_BINARY 3 /* non-standard libyang internal format */
+
+/*
+ * Now we're using LYD_FORMAT directly to avoid mapping code, but having our
+ * own definitions allows us to create such a mapping in the future if libyang
+ * makes a backwards incompatible change.
+ */
+_Static_assert(MGMT_MSG_FORMAT_XML == LYD_XML, "Format mismatch");
+_Static_assert(MGMT_MSG_FORMAT_JSON == LYD_JSON, "Format mismatch");
+_Static_assert(MGMT_MSG_FORMAT_BINARY == LYD_LYB, "Format mismatch");
+
+/**
+ * struct mgmt_msg_header - Header common to all native messages.
+ *
+ * @code: the actual type of the message.
+ * @resv: Set to zero, ignore on receive.
+ * @vsplit: If a variable section is split in 2, the length of first part.
+ * @refer_id: the session, txn, conn, etc, this message is associated with.
+ * @req_id: the request this message is for.
+ */
+struct mgmt_msg_header {
+ uint16_t code;
+ uint16_t resv;
+ uint32_t vsplit;
+ uint64_t refer_id;
+ uint64_t req_id;
+};
+_Static_assert(sizeof(struct mgmt_msg_header) == 3 * 8, "Bad padding");
+_Static_assert(sizeof(struct mgmt_msg_header) ==
+ offsetof(struct mgmt_msg_header, req_id) +
+ sizeof(((struct mgmt_msg_header *)0)->req_id),
+ "Size mismatch");
+
+/**
+ * struct mgmt_msg_error - Common error message.
+ *
+ * @error: An error value.
+ * @errst: Description of error can be 0 length.
+ *
+ * This common error message can be used for replies for many msg requests
+ * (req_id).
+ */
+struct mgmt_msg_error {
+ struct mgmt_msg_header;
+ int16_t error;
+ uint8_t resv2[6];
+
+ alignas(8) char errstr[];
+};
+_Static_assert(sizeof(struct mgmt_msg_error) ==
+ offsetof(struct mgmt_msg_error, errstr),
+ "Size mismatch");
+
+/**
+ * struct mgmt_msg_get_tree - backend oper data request.
+ *
+ * @result_type: ``LYD_FORMAT`` for the returned result.
+ * @xpath: the query for the data to return.
+ */
+struct mgmt_msg_get_tree {
+ struct mgmt_msg_header;
+ uint8_t result_type;
+ uint8_t resv2[7];
+
+ alignas(8) char xpath[];
+};
+_Static_assert(sizeof(struct mgmt_msg_get_tree) ==
+ offsetof(struct mgmt_msg_get_tree, xpath),
+ "Size mismatch");
+
+/**
+ * struct mgmt_msg_tree_data - Message carrying tree data.
+ *
+ * @partial_error: If the full result could not be returned do to this error.
+ * @result_type: ``LYD_FORMAT`` for format of the @result value.
+ * @more: if this is a partial return and there will be more coming.
+ * @result: The tree data in @result_type format.
+ *
+ */
+struct mgmt_msg_tree_data {
+ struct mgmt_msg_header;
+ int8_t partial_error;
+ uint8_t result_type;
+ uint8_t more;
+ uint8_t resv2[5];
+
+ alignas(8) uint8_t result[];
+};
+_Static_assert(sizeof(struct mgmt_msg_tree_data) ==
+ offsetof(struct mgmt_msg_tree_data, result),
+ "Size mismatch");
+
+/* Flags for get-data request */
+#define GET_DATA_FLAG_STATE 0x01 /* include "config false" data */
+#define GET_DATA_FLAG_CONFIG 0x02 /* include "config true" data */
+#define GET_DATA_FLAG_EXACT 0x04 /* get exact data node instead of the full tree */
+
+/*
+ * Modes of reporting default values. Non-default values are always reported.
+ * These options reflect "with-defaults" modes as defined in RFC 6243.
+ */
+#define GET_DATA_DEFAULTS_EXPLICIT 0 /* "explicit" */
+#define GET_DATA_DEFAULTS_TRIM 1 /* "trim" */
+#define GET_DATA_DEFAULTS_ALL 2 /* "report-all" */
+#define GET_DATA_DEFAULTS_ALL_ADD_TAG 3 /* "report-all-tagged" */
+
+/**
+ * struct mgmt_msg_get_data - frontend get-data request.
+ *
+ * @result_type: ``LYD_FORMAT`` for the returned result.
+ * @flags: combination of ``GET_DATA_FLAG_*`` flags.
+ * @defaults: one of ``GET_DATA_DEFAULTS_*`` values.
+ * @xpath: the query for the data to return.
+ */
+struct mgmt_msg_get_data {
+ struct mgmt_msg_header;
+ uint8_t result_type;
+ uint8_t flags;
+ uint8_t defaults;
+ uint8_t datastore;
+ uint8_t resv2[4];
+
+ alignas(8) char xpath[];
+};
+_Static_assert(sizeof(struct mgmt_msg_get_data) ==
+ offsetof(struct mgmt_msg_get_data, xpath),
+ "Size mismatch");
+
+/**
+ * struct mgmt_msg_notify_data - Message carrying notification data.
+ *
+ * @result_type: ``LYD_FORMAT`` for format of the @result value.
+ * @data: The xpath string of the notification followed by the tree data in
+ * @result_type format.
+ */
+struct mgmt_msg_notify_data {
+ struct mgmt_msg_header;
+ uint8_t result_type;
+ uint8_t resv2[7];
+
+ alignas(8) char data[];
+};
+_Static_assert(sizeof(struct mgmt_msg_notify_data) ==
+ offsetof(struct mgmt_msg_notify_data, data),
+ "Size mismatch");
+
+/*
+ * Validate that the message ends in a NUL terminating byte
+ */
+#define MGMT_MSG_VALIDATE_NUL_TERM(msgp, len) \
+ ((len) >= sizeof(*msgp) + 1 && ((char *)msgp)[(len)-1] == 0)
+
+
+/**
+ * Send a native message error to the other end of the connection.
+ *
+ * This function is normally used by the server-side to indicate a failure to
+ * process a client request. For this server side handling of client messages
+ * which expect a reply, either that reply or this error should be returned, as
+ * closing the connection is not allowed during message handling.
+ *
+ * Args:
+ * conn: the connection.
+ * sess_or_txn_id: Session ID (to FE client) or Txn ID (from BE client)
+ * req_id: which req_id this error is associated with.
+ * short_circuit_ok: if short circuit sending is OK.
+ * error: the error value
+ * errfmt: vprintfrr style format string
+ * ap: the variable args for errfmt.
+ *
+ * Return:
+ * The return value of ``msg_conn_send_msg``.
+ */
+extern int vmgmt_msg_native_send_error(struct msg_conn *conn,
+ uint64_t sess_or_txn_id, uint64_t req_id,
+ bool short_circuit_ok, int16_t error,
+ const char *errfmt, va_list ap)
+ PRINTFRR(6, 0);
+
+/**
+ * mgmt_msg_native_alloc_msg() - Create a native appendable msg.
+ * @msg_type: The message structure type.
+ * @var_len: The initial additional length to add to the message.
+ * @mem_type: The initial additional length to add to the message.
+ *
+ * This function takes a C type (e.g., `struct mgmt_msg_get_tree`) as an
+ * argument and returns a new native message. The newly allocated message
+ * can be used with the other `native` functions.
+ *
+ * Importantly the mgmt_msg_native_append() function can be used to add data
+ * to the end of the message, and mgmt_msg_get_native_msg_len() can be used
+ * to obtain the total length of the message (i.e., the fixed sized header plus
+ * the variable length data that has been appended).
+ *
+ * Additionally, a dynamic array (darr) pointer can be obtained using
+ * mgmt_msg_get_native_darr() which allows adding and manipulating the
+ * variable data that follows the fixed sized header.
+ *
+ * Return: A `msg_type` object created using a dynamic_array.
+ */
+#define mgmt_msg_native_alloc_msg(msg_type, var_len, mem_type) \
+ ({ \
+ uint8_t *buf = NULL; \
+ (msg_type *)darr_append_nz_mt(buf, \
+ sizeof(msg_type) + (var_len), \
+ mem_type); \
+ })
+
+/**
+ * mgmt_msg_free_native_msg() - Free a native msg.
+ * @msg - pointer to message allocated by mgmt_msg_create_native_msg().
+ */
+#define mgmt_msg_native_free_msg(msg) darr_free(msg)
+
+/**
+ * mgmt_msg_native_get_msg_len() - Get the total length of the msg.
+ * @msg: the native message.
+ *
+ * Return: the total length of the message, fixed + variable length.
+ */
+#define mgmt_msg_native_get_msg_len(msg) (darr_len((uint8_t *)(msg)))
+
+/**
+ * mgmt_msg_native_append() - Append data to the end of the msg.
+ * @msg: (IN/OUT) Pointer to the native message, variable may be updated.
+ * @data: data to append.
+ * @len: length of data to append.
+ *
+ * Append @data of length @len to the native message @msg.
+ *
+ * NOTE: Be aware @msg pointer may change as a result of reallocating the
+ * message to fit the new data. Any other pointers into the old message should
+ * be discarded.
+ *
+ * Return: a pointer to the newly appended data.
+ */
+#define mgmt_msg_native_append(msg, data, len) \
+ ({ \
+ uint8_t **darrp = mgmt_msg_native_get_darrp(msg); \
+ uint8_t *p = darr_append_n(*darrp, len); \
+ memcpy(p, data, len); \
+ p; \
+ })
+
+/**
+ * mgmt_msg_native_send_msg(msg, short_circuit_ok) - Send a native msg.
+ * @conn: the mgmt_msg connection.
+ * @msg: the native message.
+ * @short_circuit_ok: True if short-circuit sending is required.
+ *
+ * Return: The error return value of msg_conn_send_msg().
+ */
+#define mgmt_msg_native_send_msg(conn, msg, short_circuit_ok) \
+ msg_conn_send_msg(conn, MGMT_MSG_VERSION_NATIVE, msg, \
+ mgmt_msg_native_get_msg_len(msg), NULL, \
+ short_circuit_ok)
+
+/**
+ * mgmt_msg_native_get_darrp() - Return a ptr to the dynamic array ptr.
+ * @msg: Pointer to the native message.
+ *
+ * NOTE: Most users can simply use mgmt_msg_native_append() instead of this.
+ *
+ * This function obtains a pointer to the dynamic byte array for this message,
+ * this array actually includes the message header if one is going to look at
+ * the length value. With that in mind any of the `darr_*()` functions/API may
+ * be used to manipulate the variable data at the end of the message.
+ *
+ * NOTE: The pointer returned is actually a pointer to the message pointer
+ * passed in to this function. This pointer to pointer is required so that
+ * realloc can be done inside the darr API.
+ *
+ * NOTE: If reallocs happen the original passed in pointer will be updated;
+ * however, any other pointers into the message will become invalid and so they
+ * should always be discarded after using the returned value.
+ *
+ * Example:
+ *
+ * void append_metric_path(struct mgmt_msg_my_msg *msg)
+ * {
+ * char *xpath = msg->xpath; // pointer into message
+ * uint8_t **darp;
+ *
+ * darrp = mgmt_msg_native_get_darrp(msg);
+ * darr_in_strcat(*darrp, "/metric");
+ *
+ * xpath = NULL; // now invalid
+ * xpath = msg->xpath;
+ * }
+ *
+ *
+ * Return: A pointer to the first argument -- which is a pointer to a pointer to
+ * a dynamic array.
+ */
+#define mgmt_msg_native_get_darrp(msg) ((uint8_t **)&(msg))
+
+/* ------------------------- */
+/* Encode and Decode Helpers */
+/* ------------------------- */
+
+/**
+ * mgmt_msg_native_xpath_encode() - encode an xpath in a xpath, data message.
+ * @msg: Pointer to the native message.
+ * @xpath: The xpath string to encode.
+ *
+ * This function starts the encoding of a message that can be decoded with
+ * `mgmt_msg_native_xpath_data_decode()`. The variable length data is comprised
+ * of a NUL terminated string followed by some data of any format. This starts
+ * the first half of the encoding, after which one can simply append the
+ * secondary data to the message.
+ */
+#define mgmt_msg_native_xpath_encode(msg, xpath) \
+ do { \
+ size_t __slen = strlen(xpath) + 1; \
+ mgmt_msg_native_append(msg, xpath, __slen); \
+ (msg)->vsplit = __slen; \
+ } while (0)
+
+/**
+ * mgmt_msg_native_xpath_data_decode() - decode an xpath, data format message.
+ * @msg: Pointer to the native message.
+ * @msglen: Length of the message.
+ * @data: [OUT] Pointer to the data section of the variable data
+ *
+ * This function decodes a message that was encoded with
+ * `mgmt_msg_native_xpath_encode()`. The variable length data is comprised of a
+ * NUL terminated string followed by some data of any format.
+ *
+ * Return:
+ * 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) \
+ ({ \
+ 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; \
+ __s = msg->data; \
+ } \
+ __s; \
+ })
+
+/**
+ * mgmt_msg_native_xpath_decode() - return the xpath from xpath, data message.
+ * @msg: Pointer to the native message.
+ * @msglen: Length of the message.
+ *
+ * This function decodes the xpath from a message that was encoded with
+ * `mgmt_msg_native_xpath_encode()`. The variable length data is comprised of a
+ * NUL terminated string followed by some data of any format.
+ *
+ * Return:
+ * The xpath string or NULL if there was an error decoding (i.e., the
+ * message is corrupt).
+ */
+#define mgmt_msg_native_xpath_decode(msg, msglen) \
+ ({ \
+ size_t __len = (msglen) - sizeof(*msg); \
+ const char *__s = msg->data; \
+ if (!msg->vsplit || msg->vsplit > __len || \
+ __s[msg->vsplit - 1] != 0) \
+ __s = NULL; \
+ __s; \
+ })
+
+/**
+ * mgmt_msg_native_data_decode() - return the data from xpath, data message.
+ * @msg: Pointer to the native message.
+ * @msglen: Length of the message.
+ *
+ * This function decodes the secondary data from a message that was encoded with
+ * `mgmt_msg_native_xpath_encode()`. The variable length data is comprised of a
+ * NUL terminated string followed by some data of any format.
+ *
+ * Return:
+ * The secondary data or NULL if there was an error decoding (i.e., the
+ * message is corrupt).
+ */
+#define mgmt_msg_native_data_decode(msg, msglen) \
+ ({ \
+ size_t __len = (msglen) - sizeof(*msg); \
+ const char *__data = msg->data + msg->vsplit; \
+ if (!msg->vsplit || msg->vsplit > __len || __data[-1] != 0) \
+ __data = NULL; \
+ __data; \
+ })
+
+/**
+ * mgmt_msg_native_data_len_decode() - len of data in xpath, data format message.
+ * @msg: Pointer to the native message.
+ * @msglen: Length of the message.
+ *
+ * This function returns the length of the secondary variable data from a
+ * message that was encoded with `mgmt_msg_native_xpath_encode()`. The variable
+ * length data is comprised of a NUL terminated string followed by some data of
+ * any format.
+ *
+ * Return:
+ * The length of the secondary variable data. The message is assumed to be
+ * validated as not corrupt already.
+ */
+#define mgmt_msg_native_data_len_decode(msg, msglen) \
+ ((msglen) - sizeof(*msg) - msg->vsplit)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FRR_MGMT_MSG_NATIVE_H_ */
diff --git a/lib/mlag.c b/lib/mlag.c
index a370bf8924..62d00ff9a4 100644
--- a/lib/mlag.c
+++ b/lib/mlag.c
@@ -92,7 +92,7 @@ stream_failure:
}
#define MLAG_MROUTE_ADD_LENGTH \
- (VRF_NAMSIZ + INTERFACE_NAMSIZ + 4 + 4 + 4 + 4 + 1 + 1 + 4)
+ (VRF_NAMSIZ + IFNAMSIZ + 4 + 4 + 4 + 4 + 1 + 1 + 4)
int mlag_lib_decode_mroute_add(struct stream *s, struct mlag_mroute_add *msg,
size_t *length)
@@ -108,14 +108,14 @@ int mlag_lib_decode_mroute_add(struct stream *s, struct mlag_mroute_add *msg,
STREAM_GETC(s, msg->am_i_dr);
STREAM_GETC(s, msg->am_i_dual_active);
STREAM_GETL(s, msg->vrf_id);
- STREAM_GET(msg->intf_name, s, INTERFACE_NAMSIZ);
+ STREAM_GET(msg->intf_name, s, IFNAMSIZ);
return 0;
stream_failure:
return -1;
}
-#define MLAG_MROUTE_DEL_LENGTH (VRF_NAMSIZ + INTERFACE_NAMSIZ + 4 + 4 + 4 + 4)
+#define MLAG_MROUTE_DEL_LENGTH (VRF_NAMSIZ + IFNAMSIZ + 4 + 4 + 4 + 4)
int mlag_lib_decode_mroute_del(struct stream *s, struct mlag_mroute_del *msg,
size_t *length)
@@ -128,7 +128,7 @@ int mlag_lib_decode_mroute_del(struct stream *s, struct mlag_mroute_del *msg,
STREAM_GETL(s, msg->group_ip);
STREAM_GETL(s, msg->owner_id);
STREAM_GETL(s, msg->vrf_id);
- STREAM_GET(msg->intf_name, s, INTERFACE_NAMSIZ);
+ STREAM_GET(msg->intf_name, s, IFNAMSIZ);
return 0;
stream_failure:
@@ -140,7 +140,7 @@ int mlag_lib_decode_mlag_status(struct stream *s, struct mlag_status *msg)
if (s == NULL || msg == NULL)
return -1;
- STREAM_GET(msg->peerlink_rif, s, INTERFACE_NAMSIZ);
+ STREAM_GET(msg->peerlink_rif, s, IFNAMSIZ);
STREAM_GETL(s, msg->my_role);
STREAM_GETL(s, msg->peer_state);
return 0;
diff --git a/lib/mlag.h b/lib/mlag.h
index 3aef0d77a8..91c550b07c 100644
--- a/lib/mlag.h
+++ b/lib/mlag.h
@@ -65,7 +65,7 @@ struct mlag_frr_status {
};
struct mlag_status {
- char peerlink_rif[INTERFACE_NAMSIZ];
+ char peerlink_rif[IFNAMSIZ];
enum mlag_role my_role;
enum mlag_state peer_state;
};
@@ -86,7 +86,7 @@ struct mlag_mroute_add {
bool am_i_dr;
bool am_i_dual_active;
vrf_id_t vrf_id;
- char intf_name[INTERFACE_NAMSIZ];
+ char intf_name[IFNAMSIZ];
};
struct mlag_mroute_del {
@@ -95,7 +95,7 @@ struct mlag_mroute_del {
uint32_t group_ip;
enum mlag_owner owner_id;
vrf_id_t vrf_id;
- char intf_name[INTERFACE_NAMSIZ];
+ char intf_name[IFNAMSIZ];
};
struct mlag_msg {
diff --git a/lib/netns_linux.c b/lib/netns_linux.c
index 297e2c5952..8fa4bc6fe0 100644
--- a/lib/netns_linux.c
+++ b/lib/netns_linux.c
@@ -5,6 +5,7 @@
*/
#include <zebra.h>
+#include <fcntl.h>
#ifdef HAVE_NETNS
#undef _GNU_SOURCE
@@ -502,12 +503,19 @@ void ns_init_management(ns_id_t default_ns_id, ns_id_t internal_ns)
void ns_terminate(void)
{
struct ns *ns;
+ struct ns_map_nsid *ns_map;
while (!RB_EMPTY(ns_head, &ns_tree)) {
ns = RB_ROOT(ns_head, &ns_tree);
ns_delete(ns);
}
+
+ while (!RB_EMPTY(ns_map_nsid_head, &ns_map_nsid_list)) {
+ ns_map = RB_ROOT(ns_map_nsid_head, &ns_map_nsid_list);
+ RB_REMOVE(ns_map_nsid_head, &ns_map_nsid_list, ns_map);
+ XFREE(MTYPE_NS, ns_map);
+ }
}
int ns_switch_to_netns(const char *name)
diff --git a/lib/netns_other.c b/lib/netns_other.c
index 30218409dd..545a962b55 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/network.c b/lib/network.c
index af1c7db443..b768693889 100644
--- a/lib/network.c
+++ b/lib/network.c
@@ -5,6 +5,7 @@
*/
#include <zebra.h>
+#include <fcntl.h>
#include "log.h"
#include "network.h"
#include "lib_errors.h"
diff --git a/lib/nexthop.c b/lib/nexthop.c
index b04c95c05e..4ddb53cd96 100644
--- a/lib/nexthop.c
+++ b/lib/nexthop.c
@@ -56,6 +56,7 @@ static int _nexthop_srv6_cmp(const struct nexthop *nh1,
const struct nexthop *nh2)
{
int ret = 0;
+ int i = 0;
if (!nh1->nh_srv6 && !nh2->nh_srv6)
return 0;
@@ -78,9 +79,26 @@ static int _nexthop_srv6_cmp(const struct nexthop *nh1,
if (ret != 0)
return ret;
- ret = memcmp(&nh1->nh_srv6->seg6_segs,
- &nh2->nh_srv6->seg6_segs,
- sizeof(struct in6_addr));
+ if (!nh1->nh_srv6->seg6_segs && !nh2->nh_srv6->seg6_segs)
+ return 0;
+
+ if (!nh1->nh_srv6->seg6_segs && nh2->nh_srv6->seg6_segs)
+ return -1;
+
+ if (nh1->nh_srv6->seg6_segs && !nh2->nh_srv6->seg6_segs)
+ return 1;
+
+ if (nh1->nh_srv6->seg6_segs->num_segs !=
+ nh2->nh_srv6->seg6_segs->num_segs)
+ return -1;
+
+ for (i = 0; i < nh1->nh_srv6->seg6_segs->num_segs; i++) {
+ ret = memcmp(&nh1->nh_srv6->seg6_segs->seg[i],
+ &nh2->nh_srv6->seg6_segs->seg[i],
+ sizeof(struct in6_addr));
+ if (ret != 0)
+ break;
+ }
return ret;
}
@@ -155,7 +173,7 @@ static int _nexthop_cmp_no_labels(const struct nexthop *next1,
ret = _nexthop_gateway_cmp(next1, next2);
if (ret != 0)
return ret;
- /* Intentional Fall-Through */
+ fallthrough;
case NEXTHOP_TYPE_IFINDEX:
if (next1->ifindex < next2->ifindex)
return -1;
@@ -277,7 +295,7 @@ int nexthop_cmp_basic(const struct nexthop *nh1,
ret = nexthop_g_addr_cmp(nh1->type, &nh1->gate, &nh2->gate);
if (ret != 0)
return ret;
- /* Intentional Fall-Through */
+ fallthrough;
case NEXTHOP_TYPE_IFINDEX:
if (nh1->ifindex < nh2->ifindex)
return -1;
@@ -362,7 +380,7 @@ struct nexthop *nexthop_new(void)
* The linux kernel does some weird stuff with adding +1 to
* all nexthop weights it gets over netlink.
* To handle this, just default everything to 1 right from
- * from the beginning so we don't have to special case
+ * the beginning so we don't have to special case
* default weights in the linux netlink code.
*
* 1 should be a valid on all platforms anyway.
@@ -568,15 +586,25 @@ void nexthop_del_srv6_seg6local(struct nexthop *nexthop)
if (!nexthop->nh_srv6)
return;
+ if (nexthop->nh_srv6->seg6local_action == ZEBRA_SEG6_LOCAL_ACTION_UNSPEC)
+ return;
+
nexthop->nh_srv6->seg6local_action = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC;
- if (sid_zero(&nexthop->nh_srv6->seg6_segs))
+ if (nexthop->nh_srv6->seg6_segs &&
+ (nexthop->nh_srv6->seg6_segs->num_segs == 0 ||
+ sid_zero(nexthop->nh_srv6->seg6_segs)))
+ XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6->seg6_segs);
+
+ if (nexthop->nh_srv6->seg6_segs == NULL)
XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6);
}
-void nexthop_add_srv6_seg6(struct nexthop *nexthop,
- const struct in6_addr *segs)
+void nexthop_add_srv6_seg6(struct nexthop *nexthop, const struct in6_addr *segs,
+ int num_segs)
{
+ int i;
+
if (!segs)
return;
@@ -584,7 +612,22 @@ void nexthop_add_srv6_seg6(struct nexthop *nexthop,
nexthop->nh_srv6 = XCALLOC(MTYPE_NH_SRV6,
sizeof(struct nexthop_srv6));
- nexthop->nh_srv6->seg6_segs = *segs;
+ /* Enforce limit on srv6 seg stack size */
+ if (num_segs > SRV6_MAX_SIDS)
+ num_segs = SRV6_MAX_SIDS;
+
+ if (!nexthop->nh_srv6->seg6_segs) {
+ nexthop->nh_srv6->seg6_segs =
+ XCALLOC(MTYPE_NH_SRV6,
+ sizeof(struct seg6_seg_stack) +
+ num_segs * sizeof(struct in6_addr));
+ }
+
+ nexthop->nh_srv6->seg6_segs->num_segs = num_segs;
+
+ for (i = 0; i < num_segs; i++)
+ memcpy(&nexthop->nh_srv6->seg6_segs->seg[i], &segs[i],
+ sizeof(struct in6_addr));
}
void nexthop_del_srv6_seg6(struct nexthop *nexthop)
@@ -592,12 +635,14 @@ void nexthop_del_srv6_seg6(struct nexthop *nexthop)
if (!nexthop->nh_srv6)
return;
- memset(&nexthop->nh_srv6->seg6_segs, 0,
- sizeof(nexthop->nh_srv6->seg6_segs));
-
- if (nexthop->nh_srv6->seg6local_action ==
- ZEBRA_SEG6_LOCAL_ACTION_UNSPEC)
- XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6);
+ if (nexthop->nh_srv6->seg6local_action == ZEBRA_SEG6_LOCAL_ACTION_UNSPEC &&
+ nexthop->nh_srv6->seg6_segs) {
+ memset(nexthop->nh_srv6->seg6_segs->seg, 0,
+ sizeof(struct in6_addr) *
+ nexthop->nh_srv6->seg6_segs->num_segs);
+ XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6->seg6_segs);
+ }
+ XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6);
}
const char *nexthop2str(const struct nexthop *nexthop, char *str, int size)
@@ -743,11 +788,32 @@ uint32_t nexthop_hash_quick(const struct nexthop *nexthop)
}
if (nexthop->nh_srv6) {
- key = jhash_1word(nexthop->nh_srv6->seg6local_action, key);
- key = jhash(&nexthop->nh_srv6->seg6local_ctx,
- sizeof(nexthop->nh_srv6->seg6local_ctx), key);
- key = jhash(&nexthop->nh_srv6->seg6_segs,
- sizeof(nexthop->nh_srv6->seg6_segs), key);
+ int segs_num = 0;
+ int i = 0;
+
+ if (nexthop->nh_srv6->seg6local_action !=
+ ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) {
+ key = jhash_1word(nexthop->nh_srv6->seg6local_action,
+ key);
+ key = jhash(&nexthop->nh_srv6->seg6local_ctx,
+ sizeof(nexthop->nh_srv6->seg6local_ctx),
+ key);
+ if (nexthop->nh_srv6->seg6_segs)
+ key = jhash(&nexthop->nh_srv6->seg6_segs->seg[0],
+ sizeof(struct in6_addr), key);
+ } else {
+ if (nexthop->nh_srv6->seg6_segs) {
+ segs_num = nexthop->nh_srv6->seg6_segs->num_segs;
+ while (segs_num >= 1) {
+ key = jhash(&nexthop->nh_srv6->seg6_segs
+ ->seg[i],
+ sizeof(struct in6_addr),
+ key);
+ segs_num -= 1;
+ i += 1;
+ }
+ }
+ }
}
return key;
@@ -797,6 +863,7 @@ void nexthop_copy_no_recurse(struct nexthop *copy,
memcpy(&copy->gate, &nexthop->gate, sizeof(nexthop->gate));
memcpy(&copy->src, &nexthop->src, sizeof(nexthop->src));
memcpy(&copy->rmap_src, &nexthop->rmap_src, sizeof(nexthop->rmap_src));
+ memcpy(&copy->rmac, &nexthop->rmac, sizeof(nexthop->rmac));
copy->rparent = rparent;
if (nexthop->nh_label)
nexthop_add_labels(copy, nexthop->nh_label_type,
@@ -809,9 +876,13 @@ void nexthop_copy_no_recurse(struct nexthop *copy,
nexthop_add_srv6_seg6local(copy,
nexthop->nh_srv6->seg6local_action,
&nexthop->nh_srv6->seg6local_ctx);
- if (!sid_zero(&nexthop->nh_srv6->seg6_segs))
+ if (nexthop->nh_srv6->seg6_segs &&
+ nexthop->nh_srv6->seg6_segs->num_segs &&
+ !sid_zero(nexthop->nh_srv6->seg6_segs))
nexthop_add_srv6_seg6(copy,
- &nexthop->nh_srv6->seg6_segs);
+ &nexthop->nh_srv6->seg6_segs->seg[0],
+ nexthop->nh_srv6->seg6_segs
+ ->num_segs);
}
}
@@ -932,6 +1003,8 @@ ssize_t printfrr_nhs(struct fbuf *buf, const struct nexthop *nexthop)
ret += bputs(buf, "blackhole");
break;
}
+
+ ret += bprintfrr(buf, " vrfid %u", nexthop->vrf_id);
return ret;
}
@@ -1076,3 +1149,12 @@ static ssize_t printfrr_nh(struct fbuf *buf, struct printfrr_eargs *ea,
}
return -1;
}
+
+bool nexthop_is_ifindex_type(const struct nexthop *nh)
+{
+ if (nh->type == NEXTHOP_TYPE_IFINDEX ||
+ nh->type == NEXTHOP_TYPE_IPV4_IFINDEX ||
+ nh->type == NEXTHOP_TYPE_IPV6_IFINDEX)
+ return true;
+ return false;
+}
diff --git a/lib/nexthop.h b/lib/nexthop.h
index 1d95a3eee9..bed6447d49 100644
--- a/lib/nexthop.h
+++ b/lib/nexthop.h
@@ -125,6 +125,12 @@ struct nexthop {
vni_t vni;
} nh_encap;
+ /* EVPN router's MAC.
+ * Don't support multiple RMAC from the same VTEP yet, so it's not
+ * included in hash key.
+ */
+ struct ethaddr rmac;
+
/* SR-TE color used for matching SR-TE policies */
uint32_t srte_color;
@@ -151,8 +157,8 @@ void nexthop_del_labels(struct nexthop *);
void nexthop_add_srv6_seg6local(struct nexthop *nexthop, uint32_t action,
const struct seg6local_context *ctx);
void nexthop_del_srv6_seg6local(struct nexthop *nexthop);
-void nexthop_add_srv6_seg6(struct nexthop *nexthop,
- const struct in6_addr *segs);
+void nexthop_add_srv6_seg6(struct nexthop *nexthop, const struct in6_addr *seg,
+ int num_segs);
void nexthop_del_srv6_seg6(struct nexthop *nexthop);
/*
@@ -234,6 +240,9 @@ extern struct nexthop *nexthop_dup(const struct nexthop *nexthop,
extern struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop,
struct nexthop *rparent);
+/* Check nexthop of IFINDEX type */
+extern bool nexthop_is_ifindex_type(const struct nexthop *nh);
+
/*
* Parse one or more backup index values, as comma-separated numbers,
* into caller's array of uint8_ts. The array must be NEXTHOP_MAX_BACKUPS
diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c
index 0613fc6736..3f408e0a71 100644
--- a/lib/nexthop_group.c
+++ b/lib/nexthop_group.c
@@ -81,7 +81,8 @@ uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg)
return num;
}
-uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg)
+static uint8_t
+nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg)
{
struct nexthop *nhop;
uint8_t num = 0;
@@ -105,20 +106,6 @@ uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg)
return num;
}
-uint8_t
-nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group *nhg)
-{
- struct nexthop *nhop;
- uint8_t num = 0;
-
- for (nhop = nhg->nexthop; nhop; nhop = nhop->next) {
- if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
- num++;
- }
-
- return num;
-}
-
bool nexthop_group_has_label(const struct nexthop_group *nhg)
{
struct nexthop *nhop;
@@ -180,7 +167,7 @@ static struct nexthop *nhg_nh_find(const struct nexthop_group *nhg,
&nexthop->gate, &nh->gate);
if (ret != 0)
continue;
- /* Intentional Fall-Through */
+ fallthrough;
case NEXTHOP_TYPE_IFINDEX:
if (nexthop->ifindex != nh->ifindex)
continue;
@@ -1047,6 +1034,7 @@ void nexthop_group_write_nexthop_simple(struct vty *vty,
vty_out(vty, "%pI6 %s", &nh->gate.ipv6, ifname);
break;
case NEXTHOP_TYPE_BLACKHOLE:
+ vty_out(vty, "%s", "drop");
break;
}
}
@@ -1247,9 +1235,9 @@ void nexthop_group_disable_vrf(struct vrf *vrf)
struct nexthop_hold *nhh;
RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
- struct listnode *node;
+ struct listnode *node, *nnode;
- for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
+ for (ALL_LIST_ELEMENTS(nhgc->nhg_list, node, nnode, nhh)) {
struct nexthop nhop;
struct nexthop *nh;
@@ -1270,6 +1258,10 @@ void nexthop_group_disable_vrf(struct vrf *vrf)
nhg_hooks.del_nexthop(nhgc, nh);
nexthop_free(nh);
+
+ list_delete_node(nhgc->nhg_list, node);
+
+ nhgl_delete(nhh);
}
}
}
diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h
index 78237e464f..822a35439c 100644
--- a/lib/nexthop_group.h
+++ b/lib/nexthop_group.h
@@ -151,11 +151,7 @@ extern void nexthop_group_json_nexthop(json_object *j,
/* Return the number of nexthops in this nhg */
extern uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg);
extern uint8_t
-nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg);
-extern uint8_t
nexthop_group_active_nexthop_num(const struct nexthop_group *nhg);
-extern uint8_t
-nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group *nhg);
extern bool nexthop_group_has_label(const struct nexthop_group *nhg);
diff --git a/lib/northbound.c b/lib/northbound.c
index 775f6ff92f..11dca732ba 100644
--- a/lib/northbound.c
+++ b/lib/northbound.c
@@ -6,6 +6,7 @@
#include <zebra.h>
+#include "darr.h"
#include "libfrr.h"
#include "log.h"
#include "lib_errors.h"
@@ -70,12 +71,6 @@ static int nb_transaction_process(enum nb_event event,
char *errmsg, size_t errmsg_len);
static void nb_transaction_apply_finish(struct nb_transaction *transaction,
char *errmsg, size_t errmsg_len);
-static int nb_oper_data_iter_node(const struct lysc_node *snode,
- const char *xpath, const void *list_entry,
- const struct yang_list_keys *list_keys,
- struct yang_translator *translator,
- bool first, uint32_t flags,
- nb_oper_data_cb cb, void *arg);
static int nb_node_check_config_only(const struct lysc_node *snode, void *arg)
{
@@ -130,8 +125,8 @@ static int nb_node_new_cb(const struct lysc_node *snode, void *arg)
assert(snode->priv == NULL);
((struct lysc_node *)snode)->priv = nb_node;
- if (module && module->ignore_cbs)
- SET_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS);
+ if (module && module->ignore_cfg_cbs)
+ SET_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS);
return YANG_ITER_CONTINUE;
}
@@ -162,18 +157,45 @@ void nb_nodes_delete(void)
struct nb_node *nb_node_find(const char *path)
{
const struct lysc_node *snode;
+ uint32_t llopts = 0;
/*
* Use libyang to find the schema node associated to the path and get
- * the northbound node from there (snode private pointer).
+ * the northbound node from there (snode private pointer). We need to
+ * disable logging temporarily to avoid libyang from logging an error
+ * message when the node is not found.
*/
+ ly_temp_log_options(&llopts);
+
snode = yang_find_snode(ly_native_ctx, path, 0);
+
+ ly_temp_log_options(NULL);
if (!snode)
return NULL;
return snode->priv;
}
+struct nb_node **nb_nodes_find(const char *xpath)
+{
+ struct lysc_node **snodes = NULL;
+ struct nb_node **nb_nodes = NULL;
+ bool simple;
+ LY_ERR err;
+ uint i;
+
+ err = yang_resolve_snode_xpath(ly_native_ctx, xpath, &snodes, &simple);
+ if (err)
+ return NULL;
+
+ darr_ensure_i(nb_nodes, darr_lasti(snodes));
+ darr_foreach_i (snodes, i)
+ nb_nodes[i] = snodes[i]->priv;
+ darr_free(snodes);
+ return nb_nodes;
+}
+
+
void nb_node_set_dependency_cbs(const char *dependency_xpath,
const char *dependant_xpath,
struct nb_dependency_callbacks *cbs)
@@ -194,12 +216,12 @@ bool nb_node_has_dependency(struct nb_node *node)
}
static int nb_node_validate_cb(const struct nb_node *nb_node,
- enum nb_operation operation,
+ enum nb_cb_operation operation,
int callback_implemented, bool optional)
{
bool valid;
- valid = nb_operation_is_valid(operation, nb_node->snode);
+ valid = nb_cb_operation_is_valid(operation, nb_node->snode);
/*
* Add an exception for operational data callbacks. A rw list usually
@@ -211,15 +233,15 @@ static int nb_node_validate_cb(const struct nb_node *nb_node,
* depends on context (e.g. some daemons might augment "frr-interface"
* while others don't).
*/
- if (!valid && callback_implemented && operation != NB_OP_GET_NEXT
- && operation != NB_OP_GET_KEYS && operation != NB_OP_LOOKUP_ENTRY)
+ if (!valid && callback_implemented && operation != NB_CB_GET_NEXT
+ && operation != NB_CB_GET_KEYS && operation != NB_CB_LOOKUP_ENTRY)
flog_warn(EC_LIB_NB_CB_UNNEEDED,
"unneeded '%s' callback for '%s'",
- nb_operation_name(operation), nb_node->xpath);
+ nb_cb_operation_name(operation), nb_node->xpath);
if (!optional && valid && !callback_implemented) {
flog_err(EC_LIB_NB_CB_MISSING, "missing '%s' callback for '%s'",
- nb_operation_name(operation), nb_node->xpath);
+ nb_cb_operation_name(operation), nb_node->xpath);
return 1;
}
@@ -235,31 +257,33 @@ static unsigned int nb_node_validate_cbs(const struct nb_node *nb_node)
{
unsigned int error = 0;
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
return error;
- error += nb_node_validate_cb(nb_node, NB_OP_CREATE,
+ error += nb_node_validate_cb(nb_node, NB_CB_CREATE,
!!nb_node->cbs.create, false);
- error += nb_node_validate_cb(nb_node, NB_OP_MODIFY,
+ error += nb_node_validate_cb(nb_node, NB_CB_MODIFY,
!!nb_node->cbs.modify, false);
- error += nb_node_validate_cb(nb_node, NB_OP_DESTROY,
+ error += nb_node_validate_cb(nb_node, NB_CB_DESTROY,
!!nb_node->cbs.destroy, false);
- error += nb_node_validate_cb(nb_node, NB_OP_MOVE, !!nb_node->cbs.move,
+ error += nb_node_validate_cb(nb_node, NB_CB_MOVE, !!nb_node->cbs.move,
false);
- error += nb_node_validate_cb(nb_node, NB_OP_PRE_VALIDATE,
+ error += nb_node_validate_cb(nb_node, NB_CB_PRE_VALIDATE,
!!nb_node->cbs.pre_validate, true);
- error += nb_node_validate_cb(nb_node, NB_OP_APPLY_FINISH,
+ error += nb_node_validate_cb(nb_node, NB_CB_APPLY_FINISH,
!!nb_node->cbs.apply_finish, true);
- error += nb_node_validate_cb(nb_node, NB_OP_GET_ELEM,
+ error += nb_node_validate_cb(nb_node, NB_CB_GET_ELEM,
!!nb_node->cbs.get_elem, false);
- error += nb_node_validate_cb(nb_node, NB_OP_GET_NEXT,
+ error += nb_node_validate_cb(nb_node, NB_CB_GET_NEXT,
!!nb_node->cbs.get_next, false);
- error += nb_node_validate_cb(nb_node, NB_OP_GET_KEYS,
+ error += nb_node_validate_cb(nb_node, NB_CB_GET_KEYS,
!!nb_node->cbs.get_keys, false);
- error += nb_node_validate_cb(nb_node, NB_OP_LOOKUP_ENTRY,
+ error += nb_node_validate_cb(nb_node, NB_CB_LOOKUP_ENTRY,
!!nb_node->cbs.lookup_entry, false);
- error += nb_node_validate_cb(nb_node, NB_OP_RPC, !!nb_node->cbs.rpc,
+ error += nb_node_validate_cb(nb_node, NB_CB_RPC, !!nb_node->cbs.rpc,
false);
+ error += nb_node_validate_cb(nb_node, NB_CB_NOTIFY,
+ !!nb_node->cbs.notify, true);
return error;
}
@@ -305,8 +329,6 @@ struct nb_config *nb_config_new(struct lyd_node *dnode)
config->dnode = yang_dnode_new(ly_native_ctx, true);
config->version = 0;
- RB_INIT(nb_config_cbs, &config->cfg_chgs);
-
return config;
}
@@ -314,7 +336,7 @@ void nb_config_free(struct nb_config *config)
{
if (config->dnode)
yang_dnode_free(config->dnode);
- nb_config_diff_del_changes(&config->cfg_chgs);
+
XFREE(MTYPE_NB_CONFIG, config);
}
@@ -326,8 +348,6 @@ struct nb_config *nb_config_dup(const struct nb_config *config)
dup->dnode = yang_dnode_dup(config->dnode);
dup->version = config->version;
- RB_INIT(nb_config_cbs, &dup->cfg_chgs);
-
return dup;
}
@@ -369,13 +389,29 @@ void nb_config_replace(struct nb_config *config_dst,
static inline int nb_config_cb_compare(const struct nb_config_cb *a,
const struct nb_config_cb *b)
{
- /* Sort by priority first. */
- if (a->nb_node->priority < b->nb_node->priority)
+ bool a_destroy = a->operation == NB_CB_DESTROY;
+ bool b_destroy = b->operation == NB_CB_DESTROY;
+
+ /*
+ * Sort by operation first. All "destroys" must come first, to correctly
+ * process the change of a "case" inside a "choice". The old "case" must
+ * be deleted before the new "case" is created.
+ */
+ if (a_destroy && !b_destroy)
return -1;
- if (a->nb_node->priority > b->nb_node->priority)
+ if (!a_destroy && b_destroy)
return 1;
/*
+ * Then sort by priority. If the operation is "destroy", reverse the
+ * order, so that the dependants are deleted before the dependencies.
+ */
+ if (a->nb_node->priority < b->nb_node->priority)
+ return !a_destroy ? -1 : 1;
+ if (a->nb_node->priority > b->nb_node->priority)
+ return !a_destroy ? 1 : -1;
+
+ /*
* Preserve the order of the configuration changes as told by libyang.
*/
if (a->seq < b->seq)
@@ -397,10 +433,9 @@ static inline int nb_config_cb_compare(const struct nb_config_cb *a,
}
RB_GENERATE(nb_config_cbs, nb_config_cb, entry, nb_config_cb_compare);
-static void nb_config_diff_add_change(struct nb_config_cbs *changes,
- enum nb_operation operation,
- uint32_t *seq,
- const struct lyd_node *dnode)
+void nb_config_diff_add_change(struct nb_config_cbs *changes,
+ enum nb_cb_operation operation, uint32_t *seq,
+ const struct lyd_node *dnode)
{
struct nb_config_change *change;
@@ -438,7 +473,7 @@ void nb_config_diff_del_changes(struct nb_config_cbs *changes)
void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq,
struct nb_config_cbs *changes)
{
- enum nb_operation operation;
+ enum nb_cb_operation operation;
struct lyd_node *child;
/* Ignore unimplemented nodes. */
@@ -451,10 +486,10 @@ void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq,
if (lyd_is_default(dnode))
break;
- if (nb_operation_is_valid(NB_OP_CREATE, dnode->schema))
- operation = NB_OP_CREATE;
- else if (nb_operation_is_valid(NB_OP_MODIFY, dnode->schema))
- operation = NB_OP_MODIFY;
+ if (nb_cb_operation_is_valid(NB_CB_CREATE, dnode->schema))
+ operation = NB_CB_CREATE;
+ else if (nb_cb_operation_is_valid(NB_CB_MODIFY, dnode->schema))
+ operation = NB_CB_MODIFY;
else
return;
@@ -462,8 +497,8 @@ void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq,
break;
case LYS_CONTAINER:
case LYS_LIST:
- if (nb_operation_is_valid(NB_OP_CREATE, dnode->schema))
- nb_config_diff_add_change(changes, NB_OP_CREATE, seq,
+ if (nb_cb_operation_is_valid(NB_CB_CREATE, dnode->schema))
+ nb_config_diff_add_change(changes, NB_CB_CREATE, seq,
dnode);
/* Process child nodes recursively. */
@@ -483,8 +518,8 @@ static void nb_config_diff_deleted(const struct lyd_node *dnode, uint32_t *seq,
if (!dnode->schema->priv)
return;
- if (nb_operation_is_valid(NB_OP_DESTROY, dnode->schema))
- nb_config_diff_add_change(changes, NB_OP_DESTROY, seq, dnode);
+ if (nb_cb_operation_is_valid(NB_CB_DESTROY, dnode->schema))
+ nb_config_diff_add_change(changes, NB_CB_DESTROY, seq, dnode);
else if (CHECK_FLAG(dnode->schema->nodetype, LYS_CONTAINER)) {
struct lyd_node *child;
@@ -633,7 +668,7 @@ void nb_config_diff(const struct nb_config *config1,
/* either moving an entry or changing a value */
target = yang_dnode_get(config2->dnode, path);
assert(target);
- nb_config_diff_add_change(changes, NB_OP_MODIFY,
+ nb_config_diff_add_change(changes, NB_CB_MODIFY,
&seq, target);
break;
case 'n': /* none */
@@ -648,44 +683,54 @@ void nb_config_diff(const struct nb_config *config1,
lyd_free_all(diff);
}
-int nb_candidate_edit(struct nb_config *candidate,
- const struct nb_node *nb_node,
+static int dnode_create(struct nb_config *candidate, const char *xpath,
+ const char *value, uint32_t options,
+ struct lyd_node **new_dnode)
+{
+ struct lyd_node *dnode;
+ LY_ERR err;
+
+ err = lyd_new_path(candidate->dnode, ly_native_ctx, xpath, value,
+ options, &dnode);
+ if (err) {
+ flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path(%s) failed: %d",
+ __func__, xpath, err);
+ return NB_ERR;
+ } else if (dnode) {
+ err = lyd_new_implicit_tree(dnode, LYD_IMPLICIT_NO_STATE, NULL);
+ if (err) {
+ flog_warn(EC_LIB_LIBYANG,
+ "%s: lyd_new_implicit_all failed: %d",
+ __func__, err);
+ }
+ }
+ if (new_dnode)
+ *new_dnode = dnode;
+ return NB_OK;
+}
+
+int nb_candidate_edit(struct nb_config *candidate, const struct nb_node *nb_node,
enum nb_operation operation, const char *xpath,
const struct yang_data *previous,
const struct yang_data *data)
{
- struct lyd_node *dnode, *dep_dnode;
- char xpath_edit[XPATH_MAXLEN];
+ struct lyd_node *dnode, *dep_dnode, *old_dnode;
char dep_xpath[XPATH_MAXLEN];
+ struct lyd_node *parent = NULL;
+ uint32_t options = 0;
LY_ERR err;
- /* Use special notation for leaf-lists (RFC 6020, section 9.13.5). */
- if (nb_node->snode->nodetype == LYS_LEAFLIST)
- snprintf(xpath_edit, sizeof(xpath_edit), "%s[.='%s']", xpath,
- data->value);
- else
- strlcpy(xpath_edit, xpath, sizeof(xpath_edit));
-
switch (operation) {
case NB_OP_CREATE:
case NB_OP_MODIFY:
- err = lyd_new_path(candidate->dnode, ly_native_ctx, xpath_edit,
- (void *)data->value, LYD_NEW_PATH_UPDATE,
+ options = LYD_NEW_PATH_UPDATE;
+ fallthrough;
+ case NB_OP_CREATE_EXCL:
+ err = dnode_create(candidate, xpath, data->value, options,
&dnode);
if (err) {
- flog_warn(EC_LIB_LIBYANG,
- "%s: lyd_new_path(%s) failed: %d", __func__,
- xpath_edit, err);
- return NB_ERR;
+ return err;
} else if (dnode) {
- /* Create default nodes */
- LY_ERR err = lyd_new_implicit_tree(
- dnode, LYD_IMPLICIT_NO_STATE, NULL);
- if (err) {
- flog_warn(EC_LIB_LIBYANG,
- "%s: lyd_new_implicit_all failed: %d",
- __func__, err);
- }
/*
* create dependency
*
@@ -697,33 +742,24 @@ int nb_candidate_edit(struct nb_config *candidate,
nb_node->dep_cbs.get_dependency_xpath(
dnode, dep_xpath);
- err = lyd_new_path(candidate->dnode,
- ly_native_ctx, dep_xpath,
- NULL, LYD_NEW_PATH_UPDATE,
- &dep_dnode);
- /* Create default nodes */
- if (!err && dep_dnode)
- err = lyd_new_implicit_tree(
- dep_dnode,
- LYD_IMPLICIT_NO_STATE, NULL);
+ err = dnode_create(candidate, dep_xpath, NULL,
+ LYD_NEW_PATH_UPDATE, NULL);
if (err) {
- flog_warn(
- EC_LIB_LIBYANG,
- "%s: dependency: lyd_new_path(%s) failed: %d",
- __func__, dep_xpath, err);
- return NB_ERR;
+ lyd_free_tree(dnode);
+ return err;
}
}
}
break;
case NB_OP_DESTROY:
- dnode = yang_dnode_get(candidate->dnode, xpath_edit);
- if (!dnode)
- /*
- * Return a special error code so the caller can choose
- * whether to ignore it or not.
- */
- return NB_ERR_NOT_FOUND;
+ case NB_OP_DELETE:
+ dnode = yang_dnode_get(candidate->dnode, xpath);
+ if (!dnode) {
+ if (operation == NB_OP_DELETE)
+ return NB_ERR;
+ else
+ return NB_OK;
+ }
/* destroy dependant */
if (nb_node->dep_cbs.get_dependant_xpath) {
nb_node->dep_cbs.get_dependant_xpath(dnode, dep_xpath);
@@ -734,102 +770,88 @@ int nb_candidate_edit(struct nb_config *candidate,
}
lyd_free_tree(dnode);
break;
+ case NB_OP_REPLACE:
+ old_dnode = yang_dnode_get(candidate->dnode, xpath);
+ if (old_dnode) {
+ parent = lyd_parent(old_dnode);
+ lyd_unlink_tree(old_dnode);
+ }
+ err = dnode_create(candidate, xpath, data->value, options,
+ &dnode);
+ if (!err && dnode && !old_dnode) {
+ /* create dependency if the node didn't exist */
+ nb_node = dnode->schema->priv;
+ if (nb_node->dep_cbs.get_dependency_xpath) {
+ nb_node->dep_cbs.get_dependency_xpath(
+ dnode, dep_xpath);
+
+ err = dnode_create(candidate, dep_xpath, NULL,
+ LYD_NEW_PATH_UPDATE, NULL);
+ if (err)
+ lyd_free_tree(dnode);
+ }
+ }
+ if (old_dnode) {
+ /* restore original node on error, free it otherwise */
+ if (err) {
+ if (parent)
+ lyd_insert_child(parent, old_dnode);
+ else
+ lyd_insert_sibling(candidate->dnode,
+ old_dnode, NULL);
+ return err;
+ }
+
+ lyd_free_tree(old_dnode);
+ }
+ break;
case NB_OP_MOVE:
/* TODO: update configuration. */
break;
- case NB_OP_PRE_VALIDATE:
- case NB_OP_APPLY_FINISH:
- case NB_OP_GET_ELEM:
- case NB_OP_GET_NEXT:
- case NB_OP_GET_KEYS:
- case NB_OP_LOOKUP_ENTRY:
- case NB_OP_RPC:
- flog_warn(EC_LIB_DEVELOPMENT,
- "%s: unknown operation (%u) [xpath %s]", __func__,
- operation, xpath_edit);
- return NB_ERR;
}
return NB_OK;
}
-static void nb_update_candidate_changes(struct nb_config *candidate,
- struct nb_cfg_change *change,
- uint32_t *seq)
+const char *nb_operation_name(enum nb_operation operation)
{
- enum nb_operation oper = change->operation;
- char *xpath = change->xpath;
- struct lyd_node *root = NULL;
- struct lyd_node *dnode;
- struct nb_config_cbs *cfg_chgs = &candidate->cfg_chgs;
- int op;
-
- switch (oper) {
+ switch (operation) {
+ case NB_OP_CREATE_EXCL:
+ return "create exclusive";
case NB_OP_CREATE:
+ return "create";
case NB_OP_MODIFY:
- root = yang_dnode_get(candidate->dnode, xpath);
- break;
+ return "modify";
case NB_OP_DESTROY:
- root = yang_dnode_get(running_config->dnode, xpath);
- /* code */
- break;
+ return "destroy";
+ case NB_OP_DELETE:
+ return "delete";
+ case NB_OP_REPLACE:
+ return "replace";
case NB_OP_MOVE:
- case NB_OP_PRE_VALIDATE:
- case NB_OP_APPLY_FINISH:
- case NB_OP_GET_ELEM:
- case NB_OP_GET_NEXT:
- case NB_OP_GET_KEYS:
- case NB_OP_LOOKUP_ENTRY:
- case NB_OP_RPC:
- break;
- default:
- assert(!"non-enum value, invalid");
+ return "move";
}
- if (!root)
- return;
-
- LYD_TREE_DFS_BEGIN (root, dnode) {
- op = nb_lyd_diff_get_op(dnode);
- switch (op) {
- case 'c':
- nb_config_diff_created(dnode, seq, cfg_chgs);
- LYD_TREE_DFS_continue = 1;
- break;
- case 'd':
- nb_config_diff_deleted(dnode, seq, cfg_chgs);
- LYD_TREE_DFS_continue = 1;
- break;
- case 'r':
- nb_config_diff_add_change(cfg_chgs, NB_OP_MODIFY, seq,
- dnode);
- break;
- default:
- break;
- }
- LYD_TREE_DFS_END(root, dnode);
- }
+ assert(!"Reached end of function we should never hit");
}
-static bool nb_is_operation_allowed(struct nb_node *nb_node,
- struct nb_cfg_change *change)
+bool nb_is_operation_allowed(struct nb_node *nb_node, enum nb_operation oper)
{
- enum nb_operation oper = change->operation;
-
if (lysc_is_key(nb_node->snode)) {
- if (oper == NB_OP_MODIFY || oper == NB_OP_DESTROY)
+ if (oper == NB_OP_MODIFY || oper == NB_OP_DESTROY
+ || oper == NB_OP_DELETE || oper == NB_OP_REPLACE)
return false;
}
return true;
}
-void nb_candidate_edit_config_changes(
- struct nb_config *candidate_config, struct nb_cfg_change cfg_changes[],
- size_t num_cfg_changes, const char *xpath_base, const char *curr_xpath,
- int xpath_index, char *err_buf, int err_bufsize, bool *error)
+void nb_candidate_edit_config_changes(struct nb_config *candidate_config,
+ struct nb_cfg_change cfg_changes[],
+ size_t num_cfg_changes,
+ const char *xpath_base, bool in_backend,
+ char *err_buf, int err_bufsize,
+ bool *error)
{
- uint32_t seq = 0;
-
if (error)
*error = false;
@@ -840,37 +862,38 @@ void nb_candidate_edit_config_changes(
for (size_t i = 0; i < num_cfg_changes; i++) {
struct nb_cfg_change *change = &cfg_changes[i];
struct nb_node *nb_node;
+ char *change_xpath = change->xpath;
char xpath[XPATH_MAXLEN];
+ const char *value;
struct yang_data *data;
int ret;
- /* Handle relative XPaths. */
memset(xpath, 0, sizeof(xpath));
- if (xpath_index > 0 &&
- (xpath_base[0] == '.' || change->xpath[0] == '.'))
- strlcpy(xpath, curr_xpath, sizeof(xpath));
- if (xpath_base[0]) {
- if (xpath_base[0] == '.')
- strlcat(xpath, xpath_base + 1, sizeof(xpath));
- else
- strlcat(xpath, xpath_base, sizeof(xpath));
+ /* If change xpath is relative, prepend base xpath. */
+ if (change_xpath[0] == '.') {
+ strlcpy(xpath, xpath_base, sizeof(xpath));
+ change_xpath++; /* skip '.' */
}
- if (change->xpath[0] == '.')
- strlcat(xpath, change->xpath + 1, sizeof(xpath));
- else
- strlcpy(xpath, change->xpath, sizeof(xpath));
+ strlcat(xpath, change_xpath, sizeof(xpath));
/* Find the northbound node associated to the data path. */
nb_node = nb_node_find(xpath);
if (!nb_node) {
- flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
- "%s: unknown data path: %s", __func__, xpath);
- if (error)
- *error = true;
+ if (in_backend)
+ DEBUGD(&nb_dbg_cbs_config,
+ "%s: ignoring non-handled path: %s",
+ __func__, xpath);
+ else {
+ flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
+ "%s: unknown data path: %s", __func__,
+ xpath);
+ if (error)
+ *error = true;
+ }
continue;
}
/* Find if the node to be edited is not a key node */
- if (!nb_is_operation_allowed(nb_node, change)) {
+ if (!nb_is_operation_allowed(nb_node, change->operation)) {
zlog_err(" Xpath %s points to key node", xpath);
if (error)
*error = true;
@@ -878,9 +901,10 @@ void nb_candidate_edit_config_changes(
}
/* If the value is not set, get the default if it exists. */
- if (change->value == NULL)
- change->value = yang_snode_get_default(nb_node->snode);
- data = yang_data_new(xpath, change->value);
+ value = change->value;
+ if (value == NULL)
+ value = yang_snode_get_default(nb_node->snode);
+ data = yang_data_new(xpath, value);
/*
* Ignore "not found" errors when editing the candidate
@@ -889,7 +913,7 @@ void nb_candidate_edit_config_changes(
ret = nb_candidate_edit(candidate_config, nb_node,
change->operation, xpath, NULL, data);
yang_data_free(data);
- if (ret != NB_OK && ret != NB_ERR_NOT_FOUND) {
+ if (ret != NB_OK) {
flog_warn(
EC_LIB_NB_CANDIDATE_EDIT_ERROR,
"%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
@@ -899,17 +923,11 @@ void nb_candidate_edit_config_changes(
*error = true;
continue;
}
- nb_update_candidate_changes(candidate_config, change, &seq);
}
if (error && *error) {
char buf[BUFSIZ];
- /*
- * Failure to edit the candidate configuration should never
- * happen in practice, unless there's a bug in the code. When
- * that happens, log the error but otherwise ignore it.
- */
snprintf(err_buf, err_bufsize,
"%% Failed to edit configuration.\n\n%s",
yang_print_errors(ly_native_ctx, buf, sizeof(buf)));
@@ -946,9 +964,19 @@ int nb_candidate_update(struct nb_config *candidate)
int nb_candidate_validate_yang(struct nb_config *candidate, bool no_state,
char *errmsg, size_t errmsg_len)
{
- if (lyd_validate_all(&candidate->dnode, ly_native_ctx,
- no_state ? LYD_VALIDATE_NO_STATE
- : LYD_VALIDATE_PRESENT,
+ uint32_t options = 0;
+
+#ifdef LYD_VALIDATE_MULTI_ERROR
+ /* libyang 2.1.36+ */
+ options |= LYD_VALIDATE_MULTI_ERROR;
+#endif
+
+ if (no_state)
+ SET_FLAG(options, LYD_VALIDATE_NO_STATE);
+ else
+ SET_FLAG(options, LYD_VALIDATE_PRESENT);
+
+ if (lyd_validate_all(&candidate->dnode, ly_native_ctx, options,
NULL) != 0) {
yang_print_errors(ly_native_ctx, errmsg, errmsg_len);
return NB_ERR_VALIDATION;
@@ -1190,7 +1218,7 @@ int nb_running_lock_check(enum nb_client client, const void *user)
}
static void nb_log_config_callback(const enum nb_event event,
- enum nb_operation operation,
+ enum nb_cb_operation operation,
const struct lyd_node *dnode)
{
const char *value;
@@ -1207,7 +1235,7 @@ static void nb_log_config_callback(const enum nb_event event,
zlog_debug(
"northbound callback: event [%s] op [%s] xpath [%s] value [%s]",
- nb_event_name(event), nb_operation_name(operation), xpath,
+ nb_event_name(event), nb_cb_operation_name(operation), xpath,
value);
}
@@ -1221,9 +1249,9 @@ static int nb_callback_create(struct nb_context *context,
bool unexpected_error = false;
int ret;
- assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS));
+ assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS));
- nb_log_config_callback(event, NB_OP_CREATE, dnode);
+ nb_log_config_callback(event, NB_CB_CREATE, dnode);
args.context = context;
args.event = event;
@@ -1272,9 +1300,9 @@ static int nb_callback_modify(struct nb_context *context,
bool unexpected_error = false;
int ret;
- assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS));
+ assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS));
- nb_log_config_callback(event, NB_OP_MODIFY, dnode);
+ nb_log_config_callback(event, NB_CB_MODIFY, dnode);
args.context = context;
args.event = event;
@@ -1323,9 +1351,9 @@ static int nb_callback_destroy(struct nb_context *context,
bool unexpected_error = false;
int ret;
- assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS));
+ assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS));
- nb_log_config_callback(event, NB_OP_DESTROY, dnode);
+ nb_log_config_callback(event, NB_CB_DESTROY, dnode);
args.context = context;
args.event = event;
@@ -1368,9 +1396,9 @@ static int nb_callback_move(struct nb_context *context,
bool unexpected_error = false;
int ret;
- assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS));
+ assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS));
- nb_log_config_callback(event, NB_OP_MOVE, dnode);
+ nb_log_config_callback(event, NB_CB_MOVE, dnode);
args.context = context;
args.event = event;
@@ -1413,10 +1441,10 @@ static int nb_callback_pre_validate(struct nb_context *context,
bool unexpected_error = false;
int ret;
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
return 0;
- nb_log_config_callback(NB_EV_VALIDATE, NB_OP_PRE_VALIDATE, dnode);
+ nb_log_config_callback(NB_EV_VALIDATE, NB_CB_PRE_VALIDATE, dnode);
args.dnode = dnode;
args.errmsg = errmsg;
@@ -1447,10 +1475,10 @@ static void nb_callback_apply_finish(struct nb_context *context,
{
struct nb_cb_apply_finish_args args = {};
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
return;
- nb_log_config_callback(NB_EV_APPLY, NB_OP_APPLY_FINISH, dnode);
+ nb_log_config_callback(NB_EV_APPLY, NB_CB_APPLY_FINISH, dnode);
args.context = context;
args.dnode = dnode;
@@ -1465,7 +1493,7 @@ struct yang_data *nb_callback_get_elem(const struct nb_node *nb_node,
{
struct nb_cb_get_elem_args args = {};
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
return NULL;
DEBUGD(&nb_dbg_cbs_state,
@@ -1483,7 +1511,7 @@ const void *nb_callback_get_next(const struct nb_node *nb_node,
{
struct nb_cb_get_next_args args = {};
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
return NULL;
DEBUGD(&nb_dbg_cbs_state,
@@ -1500,7 +1528,7 @@ int nb_callback_get_keys(const struct nb_node *nb_node, const void *list_entry,
{
struct nb_cb_get_keys_args args = {};
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
return 0;
DEBUGD(&nb_dbg_cbs_state,
@@ -1518,7 +1546,7 @@ const void *nb_callback_lookup_entry(const struct nb_node *nb_node,
{
struct nb_cb_lookup_entry_args args = {};
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
return NULL;
DEBUGD(&nb_dbg_cbs_state,
@@ -1530,13 +1558,57 @@ const void *nb_callback_lookup_entry(const struct nb_node *nb_node,
return nb_node->cbs.lookup_entry(&args);
}
+const void *nb_callback_lookup_node_entry(struct lyd_node *node,
+ const void *parent_list_entry)
+{
+ struct yang_list_keys keys;
+ struct nb_cb_lookup_entry_args args = {};
+ const struct nb_node *nb_node = node->schema->priv;
+
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
+ return NULL;
+
+ if (yang_get_node_keys(node, &keys)) {
+ flog_warn(EC_LIB_LIBYANG,
+ "%s: can't get keys for lookup from existing data node %s",
+ __func__, node->schema->name);
+ return NULL;
+ }
+
+ DEBUGD(&nb_dbg_cbs_state,
+ "northbound callback (lookup_node_entry): node [%s] parent_list_entry [%p]",
+ nb_node->xpath, parent_list_entry);
+
+ args.parent_list_entry = parent_list_entry;
+ args.keys = &keys;
+ return nb_node->cbs.lookup_entry(&args);
+}
+
+const void *nb_callback_lookup_next(const struct nb_node *nb_node,
+ const void *parent_list_entry,
+ const struct yang_list_keys *keys)
+{
+ struct nb_cb_lookup_entry_args args = {};
+
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
+ return NULL;
+
+ DEBUGD(&nb_dbg_cbs_state,
+ "northbound callback (lookup_entry): node [%s] parent_list_entry [%p]",
+ nb_node->xpath, parent_list_entry);
+
+ args.parent_list_entry = parent_list_entry;
+ args.keys = keys;
+ return nb_node->cbs.lookup_next(&args);
+}
+
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)
{
struct nb_cb_rpc_args args = {};
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
return 0;
DEBUGD(&nb_dbg_cbs_rpc, "northbound RPC: %s", xpath);
@@ -1549,6 +1621,18 @@ int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
return nb_node->cbs.rpc(&args);
}
+void nb_callback_notify(const struct nb_node *nb_node, const char *xpath,
+ struct lyd_node *dnode)
+{
+ struct nb_cb_notify_args args = {};
+
+ DEBUGD(&nb_dbg_cbs_notify, "northbound notify: %s", xpath);
+
+ args.xpath = xpath;
+ args.dnode = dnode;
+ nb_node->cbs.notify(&args);
+}
+
/*
* Call the northbound configuration callback associated to a given
* configuration change.
@@ -1558,14 +1642,14 @@ static int nb_callback_configuration(struct nb_context *context,
struct nb_config_change *change,
char *errmsg, size_t errmsg_len)
{
- enum nb_operation operation = change->cb.operation;
+ enum nb_cb_operation operation = change->cb.operation;
char xpath[XPATH_MAXLEN];
const struct nb_node *nb_node = change->cb.nb_node;
const struct lyd_node *dnode = change->cb.dnode;
union nb_resource *resource;
int ret = NB_ERR;
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
return NB_OK;
if (event == NB_EV_VALIDATE)
@@ -1574,29 +1658,30 @@ static int nb_callback_configuration(struct nb_context *context,
resource = &change->resource;
switch (operation) {
- case NB_OP_CREATE:
+ case NB_CB_CREATE:
ret = nb_callback_create(context, nb_node, event, dnode,
resource, errmsg, errmsg_len);
break;
- case NB_OP_MODIFY:
+ case NB_CB_MODIFY:
ret = nb_callback_modify(context, nb_node, event, dnode,
resource, errmsg, errmsg_len);
break;
- case NB_OP_DESTROY:
+ case NB_CB_DESTROY:
ret = nb_callback_destroy(context, nb_node, event, dnode,
errmsg, errmsg_len);
break;
- case NB_OP_MOVE:
+ case NB_CB_MOVE:
ret = nb_callback_move(context, nb_node, event, dnode, errmsg,
errmsg_len);
break;
- case NB_OP_PRE_VALIDATE:
- case NB_OP_APPLY_FINISH:
- case NB_OP_GET_ELEM:
- case NB_OP_GET_NEXT:
- case NB_OP_GET_KEYS:
- case NB_OP_LOOKUP_ENTRY:
- case NB_OP_RPC:
+ case NB_CB_PRE_VALIDATE:
+ case NB_CB_APPLY_FINISH:
+ case NB_CB_GET_ELEM:
+ case NB_CB_GET_NEXT:
+ case NB_CB_GET_KEYS:
+ case NB_CB_LOOKUP_ENTRY:
+ case NB_CB_RPC:
+ case NB_CB_NOTIFY:
yang_dnode_get_path(dnode, xpath, sizeof(xpath));
flog_err(EC_LIB_DEVELOPMENT,
"%s: unknown operation (%u) [xpath %s]", __func__,
@@ -1612,28 +1697,28 @@ static int nb_callback_configuration(struct nb_context *context,
flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
"error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s",
nb_err_name(ret), nb_event_name(event),
- nb_operation_name(operation), xpath,
+ nb_cb_operation_name(operation), xpath,
errmsg[0] ? " message: " : "", errmsg);
break;
case NB_EV_PREPARE:
flog_warn(EC_LIB_NB_CB_CONFIG_PREPARE,
"error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s",
nb_err_name(ret), nb_event_name(event),
- nb_operation_name(operation), xpath,
+ nb_cb_operation_name(operation), xpath,
errmsg[0] ? " message: " : "", errmsg);
break;
case NB_EV_ABORT:
flog_warn(EC_LIB_NB_CB_CONFIG_ABORT,
"error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s",
nb_err_name(ret), nb_event_name(event),
- nb_operation_name(operation), xpath,
+ nb_cb_operation_name(operation), xpath,
errmsg[0] ? " message: " : "", errmsg);
break;
case NB_EV_APPLY:
flog_err(EC_LIB_NB_CB_CONFIG_APPLY,
"error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s",
nb_err_name(ret), nb_event_name(event),
- nb_operation_name(operation), xpath,
+ nb_cb_operation_name(operation), xpath,
errmsg[0] ? " message: " : "", errmsg);
break;
default:
@@ -1740,6 +1825,7 @@ nb_apply_finish_cb_new(struct nb_config_cbs *cbs, const struct nb_node *nb_node,
struct nb_config_cb *cb;
cb = XCALLOC(MTYPE_TMP, sizeof(*cb));
+ cb->operation = NB_CB_APPLY_FINISH;
cb->nb_node = nb_node;
cb->dnode = dnode;
RB_INSERT(nb_config_cbs, cbs, cb);
@@ -1754,6 +1840,7 @@ nb_apply_finish_cb_find(struct nb_config_cbs *cbs,
{
struct nb_config_cb s;
+ s.operation = NB_CB_APPLY_FINISH;
s.seq = 0;
s.nb_node = nb_node;
s.dnode = dnode;
@@ -1781,12 +1868,12 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction,
* (the 'apply_finish' callbacks from the node ancestors should
* be called though).
*/
- if (change->cb.operation == NB_OP_DESTROY) {
+ if (change->cb.operation == NB_CB_DESTROY) {
char xpath[XPATH_MAXLEN];
dnode = lyd_parent(dnode);
if (!dnode)
- break;
+ continue;
/*
* The dnode from 'delete' callbacks point to elements
@@ -1832,394 +1919,15 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction,
}
}
-static int nb_oper_data_iter_children(const struct lysc_node *snode,
- const char *xpath, const void *list_entry,
- const struct yang_list_keys *list_keys,
- struct yang_translator *translator,
- bool first, uint32_t flags,
- nb_oper_data_cb cb, void *arg)
-{
- const struct lysc_node *child;
-
- LY_LIST_FOR (lysc_node_child(snode), child) {
- int ret;
-
- ret = nb_oper_data_iter_node(child, xpath, list_entry,
- list_keys, translator, false,
- flags, cb, arg);
- if (ret != NB_OK)
- return ret;
- }
-
- return NB_OK;
-}
-
-static int nb_oper_data_iter_leaf(const struct nb_node *nb_node,
- const char *xpath, const void *list_entry,
- const struct yang_list_keys *list_keys,
- struct yang_translator *translator,
- uint32_t flags, nb_oper_data_cb cb, void *arg)
-{
- struct yang_data *data;
-
- if (CHECK_FLAG(nb_node->snode->flags, LYS_CONFIG_W))
- return NB_OK;
-
- /* Ignore list keys. */
- if (lysc_is_key(nb_node->snode))
- return NB_OK;
-
- data = nb_callback_get_elem(nb_node, xpath, list_entry);
- if (data == NULL)
- /* Leaf of type "empty" is not present. */
- return NB_OK;
-
- return (*cb)(nb_node->snode, translator, data, arg);
-}
-
-static int nb_oper_data_iter_container(const struct nb_node *nb_node,
- const char *xpath,
- const void *list_entry,
- const struct yang_list_keys *list_keys,
- struct yang_translator *translator,
- uint32_t flags, nb_oper_data_cb cb,
- void *arg)
-{
- const struct lysc_node *snode = nb_node->snode;
-
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
- return NB_OK;
-
- /* Read-only presence containers. */
- if (nb_node->cbs.get_elem) {
- struct yang_data *data;
- int ret;
-
- data = nb_callback_get_elem(nb_node, xpath, list_entry);
- if (data == NULL)
- /* Presence container is not present. */
- return NB_OK;
-
- ret = (*cb)(snode, translator, data, arg);
- if (ret != NB_OK)
- return ret;
- }
-
- /* Read-write presence containers. */
- if (CHECK_FLAG(snode->flags, LYS_CONFIG_W)) {
- struct lysc_node_container *scontainer;
-
- scontainer = (struct lysc_node_container *)snode;
- if (CHECK_FLAG(scontainer->flags, LYS_PRESENCE)
- && !yang_dnode_get(running_config->dnode, xpath))
- return NB_OK;
- }
-
- /* Iterate over the child nodes. */
- return nb_oper_data_iter_children(snode, xpath, list_entry, list_keys,
- translator, false, flags, cb, arg);
-}
-
-static int
-nb_oper_data_iter_leaflist(const struct nb_node *nb_node, const char *xpath,
- const void *parent_list_entry,
- const struct yang_list_keys *parent_list_keys,
- struct yang_translator *translator, uint32_t flags,
- nb_oper_data_cb cb, void *arg)
-{
- const void *list_entry = NULL;
-
- if (CHECK_FLAG(nb_node->snode->flags, LYS_CONFIG_W))
- return NB_OK;
-
- do {
- struct yang_data *data;
- int ret;
-
- list_entry = nb_callback_get_next(nb_node, parent_list_entry,
- list_entry);
- if (!list_entry)
- /* End of the list. */
- break;
-
- data = nb_callback_get_elem(nb_node, xpath, list_entry);
- if (data == NULL)
- continue;
-
- ret = (*cb)(nb_node->snode, translator, data, arg);
- if (ret != NB_OK)
- return ret;
- } while (list_entry);
-
- return NB_OK;
-}
-
-static int nb_oper_data_iter_list(const struct nb_node *nb_node,
- const char *xpath_list,
- const void *parent_list_entry,
- const struct yang_list_keys *parent_list_keys,
- struct yang_translator *translator,
- uint32_t flags, nb_oper_data_cb cb, void *arg)
-{
- const struct lysc_node *snode = nb_node->snode;
- const void *list_entry = NULL;
- uint32_t position = 1;
-
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
- return NB_OK;
-
- /* Iterate over all list entries. */
- do {
- const struct lysc_node_leaf *skey;
- struct yang_list_keys list_keys = {};
- char xpath[XPATH_MAXLEN * 2];
- int ret;
-
- /* Obtain list entry. */
- list_entry = nb_callback_get_next(nb_node, parent_list_entry,
- list_entry);
- if (!list_entry)
- /* End of the list. */
- break;
-
- if (!CHECK_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST)) {
- /* Obtain the list entry keys. */
- if (nb_callback_get_keys(nb_node, list_entry,
- &list_keys)
- != NB_OK) {
- flog_warn(EC_LIB_NB_CB_STATE,
- "%s: failed to get list keys",
- __func__);
- return NB_ERR;
- }
-
- /* Build XPath of the list entry. */
- strlcpy(xpath, xpath_list, sizeof(xpath));
- unsigned int i = 0;
- LY_FOR_KEYS (snode, skey) {
- assert(i < list_keys.num);
- snprintf(xpath + strlen(xpath),
- sizeof(xpath) - strlen(xpath),
- "[%s='%s']", skey->name,
- list_keys.key[i]);
- i++;
- }
- assert(i == list_keys.num);
- } else {
- /*
- * Keyless list - build XPath using a positional index.
- */
- snprintf(xpath, sizeof(xpath), "%s[%u]", xpath_list,
- position);
- position++;
- }
-
- /* Iterate over the child nodes. */
- ret = nb_oper_data_iter_children(
- nb_node->snode, xpath, list_entry, &list_keys,
- translator, false, flags, cb, arg);
- if (ret != NB_OK)
- return ret;
- } while (list_entry);
-
- return NB_OK;
-}
-
-static int nb_oper_data_iter_node(const struct lysc_node *snode,
- const char *xpath_parent,
- const void *list_entry,
- const struct yang_list_keys *list_keys,
- struct yang_translator *translator,
- bool first, uint32_t flags,
- nb_oper_data_cb cb, void *arg)
-{
- struct nb_node *nb_node;
- char xpath[XPATH_MAXLEN];
- int ret = NB_OK;
-
- if (!first && CHECK_FLAG(flags, NB_OPER_DATA_ITER_NORECURSE)
- && CHECK_FLAG(snode->nodetype, LYS_CONTAINER | LYS_LIST))
- return NB_OK;
-
- /* Update XPath. */
- strlcpy(xpath, xpath_parent, sizeof(xpath));
- if (!first && snode->nodetype != LYS_USES) {
- struct lysc_node *parent;
-
- /* Get the real parent. */
- parent = snode->parent;
-
- /*
- * When necessary, include the namespace of the augmenting
- * module.
- */
- if (parent && parent->module != snode->module)
- snprintf(xpath + strlen(xpath),
- sizeof(xpath) - strlen(xpath), "/%s:%s",
- snode->module->name, snode->name);
- else
- snprintf(xpath + strlen(xpath),
- sizeof(xpath) - strlen(xpath), "/%s",
- snode->name);
- }
-
- nb_node = snode->priv;
- switch (snode->nodetype) {
- case LYS_CONTAINER:
- ret = nb_oper_data_iter_container(nb_node, xpath, list_entry,
- list_keys, translator, flags,
- cb, arg);
- break;
- case LYS_LEAF:
- ret = nb_oper_data_iter_leaf(nb_node, xpath, list_entry,
- list_keys, translator, flags, cb,
- arg);
- break;
- case LYS_LEAFLIST:
- ret = nb_oper_data_iter_leaflist(nb_node, xpath, list_entry,
- list_keys, translator, flags,
- cb, arg);
- break;
- case LYS_LIST:
- ret = nb_oper_data_iter_list(nb_node, xpath, list_entry,
- list_keys, translator, flags, cb,
- arg);
- break;
- case LYS_USES:
- ret = nb_oper_data_iter_children(snode, xpath, list_entry,
- list_keys, translator, false,
- flags, cb, arg);
- break;
- default:
- break;
- }
-
- return ret;
-}
-
-int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator,
- uint32_t flags, nb_oper_data_cb cb, void *arg)
-{
- struct nb_node *nb_node;
- const void *list_entry = NULL;
- struct yang_list_keys list_keys;
- struct list *list_dnodes;
- struct lyd_node *dnode, *dn;
- struct listnode *ln;
- 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 NB_ERR;
- }
-
- /* For now this function works only with containers and lists. */
- if (!CHECK_FLAG(nb_node->snode->nodetype, LYS_CONTAINER | LYS_LIST)) {
- flog_warn(
- EC_LIB_NB_OPERATIONAL_DATA,
- "%s: can't iterate over YANG leaf or leaf-list [xpath %s]",
- __func__, xpath);
- return NB_ERR;
- }
-
- /*
- * Create a data tree from the XPath so that we can parse the keys of
- * all YANG lists (if any).
- */
-
- LY_ERR err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0,
- LYD_NEW_PATH_UPDATE, NULL, &dnode);
- if (err || !dnode) {
- const char *errmsg =
- err ? ly_errmsg(ly_native_ctx) : "node not found";
- flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed %s",
- __func__, errmsg);
- return NB_ERR;
- }
-
- /*
- * Create a linked list to sort the data nodes starting from the root.
- */
- list_dnodes = list_new();
- for (dn = dnode; dn; dn = lyd_parent(dn)) {
- if (dn->schema->nodetype != LYS_LIST || !lyd_child(dn))
- continue;
- listnode_add_head(list_dnodes, dn);
- }
- /*
- * Use the northbound callbacks to find list entry pointer corresponding
- * to the given XPath.
- */
- for (ALL_LIST_ELEMENTS_RO(list_dnodes, ln, dn)) {
- struct lyd_node *child;
- struct nb_node *nn;
- unsigned int n = 0;
-
- /* Obtain the list entry keys. */
- memset(&list_keys, 0, sizeof(list_keys));
- LY_LIST_FOR (lyd_child(dn), child) {
- if (!lysc_is_key(child->schema))
- break;
- strlcpy(list_keys.key[n],
- yang_dnode_get_string(child, NULL),
- sizeof(list_keys.key[n]));
- n++;
- }
- list_keys.num = n;
- if (list_keys.num != yang_snode_num_keys(dn->schema)) {
- list_delete(&list_dnodes);
- yang_dnode_free(dnode);
- return NB_ERR_NOT_FOUND;
- }
-
- /* Find the list entry pointer. */
- nn = dn->schema->priv;
- if (!nn->cbs.lookup_entry) {
- flog_warn(
- EC_LIB_NB_OPERATIONAL_DATA,
- "%s: data path doesn't support iteration over operational data: %s",
- __func__, xpath);
- list_delete(&list_dnodes);
- yang_dnode_free(dnode);
- return NB_ERR;
- }
-
- list_entry =
- nb_callback_lookup_entry(nn, list_entry, &list_keys);
- if (list_entry == NULL) {
- list_delete(&list_dnodes);
- yang_dnode_free(dnode);
- return NB_ERR_NOT_FOUND;
- }
- }
-
- /* If a list entry was given, iterate over that list entry only. */
- if (dnode->schema->nodetype == LYS_LIST && lyd_child(dnode))
- ret = nb_oper_data_iter_children(
- nb_node->snode, xpath, list_entry, &list_keys,
- translator, true, flags, cb, arg);
- else
- ret = nb_oper_data_iter_node(nb_node->snode, xpath, list_entry,
- &list_keys, translator, true,
- flags, cb, arg);
-
- list_delete(&list_dnodes);
- yang_dnode_free(dnode);
-
- return ret;
-}
-
-bool nb_operation_is_valid(enum nb_operation operation,
- const struct lysc_node *snode)
+bool nb_cb_operation_is_valid(enum nb_cb_operation operation,
+ const struct lysc_node *snode)
{
struct nb_node *nb_node = snode->priv;
struct lysc_node_container *scontainer;
struct lysc_node_leaf *sleaf;
switch (operation) {
- case NB_OP_CREATE:
+ case NB_CB_CREATE:
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
@@ -2230,6 +1938,8 @@ bool nb_operation_is_valid(enum nb_operation operation,
return false;
break;
case LYS_CONTAINER:
+ if (snode->parent && snode->parent->nodetype == LYS_CASE)
+ return true;
scontainer = (struct lysc_node_container *)snode;
if (!CHECK_FLAG(scontainer->flags, LYS_PRESENCE))
return false;
@@ -2241,7 +1951,7 @@ bool nb_operation_is_valid(enum nb_operation operation,
return false;
}
return true;
- case NB_OP_MODIFY:
+ case NB_CB_MODIFY:
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
@@ -2259,7 +1969,7 @@ bool nb_operation_is_valid(enum nb_operation operation,
return false;
}
return true;
- case NB_OP_DESTROY:
+ case NB_CB_DESTROY:
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
@@ -2284,6 +1994,8 @@ bool nb_operation_is_valid(enum nb_operation operation,
return false;
break;
case LYS_CONTAINER:
+ if (snode->parent && snode->parent->nodetype == LYS_CASE)
+ return true;
scontainer = (struct lysc_node_container *)snode;
if (!CHECK_FLAG(scontainer->flags, LYS_PRESENCE))
return false;
@@ -2295,7 +2007,7 @@ bool nb_operation_is_valid(enum nb_operation operation,
return false;
}
return true;
- case NB_OP_MOVE:
+ case NB_CB_MOVE:
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
@@ -2309,12 +2021,12 @@ bool nb_operation_is_valid(enum nb_operation operation,
return false;
}
return true;
- case NB_OP_PRE_VALIDATE:
- case NB_OP_APPLY_FINISH:
+ case NB_CB_PRE_VALIDATE:
+ case NB_CB_APPLY_FINISH:
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
return true;
- case NB_OP_GET_ELEM:
+ case NB_CB_GET_ELEM:
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R))
return false;
@@ -2331,7 +2043,7 @@ bool nb_operation_is_valid(enum nb_operation operation,
return false;
}
return true;
- case NB_OP_GET_NEXT:
+ case NB_CB_GET_NEXT:
switch (snode->nodetype) {
case LYS_LIST:
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
@@ -2345,8 +2057,8 @@ bool nb_operation_is_valid(enum nb_operation operation,
return false;
}
return true;
- case NB_OP_GET_KEYS:
- case NB_OP_LOOKUP_ENTRY:
+ case NB_CB_GET_KEYS:
+ case NB_CB_LOOKUP_ENTRY:
switch (snode->nodetype) {
case LYS_LIST:
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
@@ -2358,7 +2070,7 @@ bool nb_operation_is_valid(enum nb_operation operation,
return false;
}
return true;
- case NB_OP_RPC:
+ case NB_CB_RPC:
if (CHECK_FLAG(snode->flags, LYS_CONFIG_W | LYS_CONFIG_R))
return false;
@@ -2370,6 +2082,10 @@ bool nb_operation_is_valid(enum nb_operation operation,
return false;
}
return true;
+ case NB_CB_NOTIFY:
+ if (snode->nodetype != LYS_NOTIF)
+ return false;
+ return true;
default:
return false;
}
@@ -2380,17 +2096,81 @@ DEFINE_HOOK(nb_notification_send, (const char *xpath, struct list *arguments),
int nb_notification_send(const char *xpath, struct list *arguments)
{
+ struct lyd_node *root = NULL;
+ struct lyd_node *dnode;
+ struct yang_data *data;
+ struct listnode *ln;
+ LY_ERR err;
int ret;
DEBUGD(&nb_dbg_notif, "northbound notification: %s", xpath);
+ /*
+ * Call old hook functions
+ */
ret = hook_call(nb_notification_send, xpath, arguments);
+
+ if (!hook_have_hooks(nb_notification_tree_send))
+ goto done;
+ /*
+ * Convert yang data arguments list to a libyang data tree for new hook
+ * functions.
+ */
+ for (ALL_LIST_ELEMENTS_RO(arguments, ln, data)) {
+ err = lyd_new_path(root, ly_native_ctx, data->xpath,
+ data->value, LYD_NEW_PATH_UPDATE, &dnode);
+ if (err != LY_SUCCESS)
+ goto lyerr;
+ if (!root) {
+ root = dnode;
+ while (root->parent)
+ root = lyd_parent(root);
+ }
+ }
+
+ if (!root) {
+ err = lyd_new_path(NULL, ly_native_ctx, xpath, "", 0, &root);
+ if (err) {
+lyerr:
+ flog_err(EC_LIB_LIBYANG,
+ "%s: error creating notification data: %s",
+ __func__, ly_strerrcode(err));
+ ret += 1;
+ goto done;
+ }
+ }
+
+ /*
+ * Call new hook functions
+ */
+ ret += nb_notification_tree_send(xpath, root);
+
+done:
+ if (root)
+ lyd_free_all(root);
if (arguments)
list_delete(&arguments);
return ret;
}
+DEFINE_HOOK(nb_notification_tree_send,
+ (const char *xpath, const struct lyd_node *tree), (xpath, tree));
+
+int nb_notification_tree_send(const char *xpath, const struct lyd_node *tree)
+{
+ int ret;
+
+ assert(tree);
+
+ DEBUGD(&nb_dbg_notif, "northbound tree notification: %s",
+ tree->schema->name);
+
+ ret = hook_call(nb_notification_tree_send, xpath, tree);
+
+ return ret;
+}
+
/* Running configuration user pointers management. */
struct nb_config_entry {
char xpath[XPATH_MAXLEN];
@@ -2560,31 +2340,33 @@ const char *nb_event_name(enum nb_event event)
assert(!"Reached end of function we should never hit");
}
-const char *nb_operation_name(enum nb_operation operation)
+const char *nb_cb_operation_name(enum nb_cb_operation operation)
{
switch (operation) {
- case NB_OP_CREATE:
+ case NB_CB_CREATE:
return "create";
- case NB_OP_MODIFY:
+ case NB_CB_MODIFY:
return "modify";
- case NB_OP_DESTROY:
+ case NB_CB_DESTROY:
return "destroy";
- case NB_OP_MOVE:
+ case NB_CB_MOVE:
return "move";
- case NB_OP_PRE_VALIDATE:
+ case NB_CB_PRE_VALIDATE:
return "pre_validate";
- case NB_OP_APPLY_FINISH:
+ case NB_CB_APPLY_FINISH:
return "apply_finish";
- case NB_OP_GET_ELEM:
+ case NB_CB_GET_ELEM:
return "get_elem";
- case NB_OP_GET_NEXT:
+ case NB_CB_GET_NEXT:
return "get_next";
- case NB_OP_GET_KEYS:
+ case NB_CB_GET_KEYS:
return "get_keys";
- case NB_OP_LOOKUP_ENTRY:
+ case NB_CB_LOOKUP_ENTRY:
return "lookup_entry";
- case NB_OP_RPC:
+ case NB_CB_RPC:
return "rpc";
+ case NB_CB_NOTIFY:
+ return "notify";
}
assert(!"Reached end of function we should never hit");
@@ -2609,6 +2391,8 @@ const char *nb_err_name(enum nb_error error)
return "failed to allocate resource";
case NB_ERR_INCONSISTENCY:
return "internal inconsistency";
+ case NB_YIELD:
+ return "should yield";
}
assert(!"Reached end of function we should never hit");
@@ -2619,8 +2403,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:
@@ -2640,10 +2422,6 @@ const char *nb_client_name(enum nb_client client)
static void nb_load_callbacks(const struct frr_yang_module_info *module)
{
-
- if (module->ignore_cbs)
- return;
-
for (size_t i = 0; module->nodes[i].xpath; i++) {
struct nb_node *nb_node;
uint32_t priority;
@@ -2690,7 +2468,6 @@ void nb_init(struct event_loop *tm,
size_t nmodules, bool db_enabled)
{
struct yang_module *loaded[nmodules], **loadedp = loaded;
- bool explicit_compile;
/*
* Currently using this explicit compile feature in libyang2 leads to
@@ -2698,8 +2475,9 @@ void nb_init(struct event_loop *tm,
* of modules until they have all been loaded into the context. This
* avoids multiple recompiles of the same modules as they are
* imported/augmented etc.
+ * (Done as a #define to make coverity happy)
*/
- explicit_compile = false;
+#define explicit_compile false
nb_db_enabled = db_enabled;
@@ -2709,7 +2487,8 @@ void nb_init(struct event_loop *tm,
for (size_t i = 0; i < nmodules; i++) {
DEBUGD(&nb_dbg_events, "northbound: loading %s.yang",
modules[i]->name);
- *loadedp++ = yang_module_load(modules[i]->name);
+ *loadedp++ = yang_module_load(modules[i]->name,
+ modules[i]->features);
}
if (explicit_compile)
@@ -2734,10 +2513,15 @@ void nb_init(struct event_loop *tm,
/* Initialize the northbound CLI. */
nb_cli_init(tm);
+
+ /* Initialize oper-state */
+ nb_oper_init(tm);
}
void nb_terminate(void)
{
+ nb_oper_terminate();
+
/* Terminate the northbound CLI. */
nb_cli_terminate();
diff --git a/lib/northbound.h b/lib/northbound.h
index 1723a87e4e..01e23c8160 100644
--- a/lib/northbound.h
+++ b/lib/northbound.h
@@ -83,28 +83,23 @@ enum nb_event {
};
/*
- * Northbound operations.
+ * Northbound callback operations.
*
* Refer to the documentation comments of nb_callbacks for more details.
*/
-enum nb_operation {
- NB_OP_CREATE,
- NB_OP_MODIFY,
- NB_OP_DESTROY,
- NB_OP_MOVE,
- NB_OP_PRE_VALIDATE,
- NB_OP_APPLY_FINISH,
- NB_OP_GET_ELEM,
- NB_OP_GET_NEXT,
- NB_OP_GET_KEYS,
- NB_OP_LOOKUP_ENTRY,
- NB_OP_RPC,
-};
-
-struct nb_cfg_change {
- char xpath[XPATH_MAXLEN];
- enum nb_operation operation;
- const char *value;
+enum nb_cb_operation {
+ NB_CB_CREATE,
+ NB_CB_MODIFY,
+ NB_CB_DESTROY,
+ NB_CB_MOVE,
+ NB_CB_PRE_VALIDATE,
+ NB_CB_APPLY_FINISH,
+ NB_CB_GET_ELEM,
+ NB_CB_GET_NEXT,
+ NB_CB_GET_KEYS,
+ NB_CB_LOOKUP_ENTRY,
+ NB_CB_RPC,
+ NB_CB_NOTIFY,
};
union nb_resource {
@@ -292,6 +287,18 @@ struct nb_cb_rpc_args {
size_t errmsg_len;
};
+struct nb_cb_notify_args {
+ /* XPath of the notification. */
+ const char *xpath;
+
+ /*
+ * libyang data node representing the notification. If the notification
+ * is not top-level, it still points to the notification node, but it's
+ * part of the full data tree with all its parents.
+ */
+ struct lyd_node *dnode;
+};
+
/*
* Set of configuration callbacks that can be associated to a northbound node.
*/
@@ -485,6 +492,22 @@ struct nb_callbacks {
const void *(*lookup_entry)(struct nb_cb_lookup_entry_args *args);
/*
+ * Operational data callback for YANG lists.
+ *
+ * The callback function should return the next list entry that would
+ * follow a list entry with the keys given as a parameter. Keyless
+ * lists don't need to implement this callback.
+ *
+ * args
+ * Refer to the documentation comments of nb_cb_lookup_entry_args for
+ * details.
+ *
+ * Returns:
+ * Pointer to the list entry if found, or NULL if not found.
+ */
+ const void *(*lookup_next)(struct nb_cb_lookup_entry_args *args);
+
+ /*
* RPC and action callback.
*
* Both 'input' and 'output' are lists of 'yang_data' structures. The
@@ -500,6 +523,17 @@ struct nb_callbacks {
int (*rpc)(struct nb_cb_rpc_args *args);
/*
+ * Notification callback.
+ *
+ * The callback is called when a YANG notification is received.
+ *
+ * args
+ * Refer to the documentation comments of nb_cb_notify_args for
+ * details.
+ */
+ void (*notify)(struct nb_cb_notify_args *args);
+
+ /*
* Optional callback to compare the data nodes when printing
* the CLI commands associated with them.
*
@@ -587,18 +621,13 @@ 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
/* The YANG list doesn't contain key leafs. */
#define F_NB_NODE_KEYLESS_LIST 0x02
-/* Ignore callbacks for this node */
-#define F_NB_NODE_IGNORE_CBS 0x04
+/* Ignore config callbacks for this node */
+#define F_NB_NODE_IGNORE_CFG_CBS 0x04
/*
* HACK: old gcc versions (< 5.x) have a bug that prevents C99 flexible arrays
@@ -612,10 +641,19 @@ struct frr_yang_module_info {
const char *name;
/*
- * Ignore callbacks for this module. Set this to true to
- * load module without any callbacks.
+ * Ignore configuration callbacks for this module. Set this to true to
+ * load module with only CLI-related callbacks. This is useful for
+ * modules loaded in mgmtd.
+ */
+ bool ignore_cfg_cbs;
+
+ /*
+ * The NULL-terminated list of supported features.
+ * Features are defined with "feature" statements in the YANG model.
+ * Use ["*", NULL] to enable all features.
+ * Use NULL to disable all features.
*/
- bool ignore_cbs;
+ const char **features;
/* Northbound callbacks. */
const struct {
@@ -644,6 +682,7 @@ enum nb_error {
NB_ERR_VALIDATION,
NB_ERR_RESOURCE,
NB_ERR_INCONSISTENCY,
+ NB_YIELD,
};
/* Default priority. */
@@ -656,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,
@@ -676,7 +714,7 @@ struct nb_context {
/* Northbound configuration callback. */
struct nb_config_cb {
RB_ENTRY(nb_config_cb) entry;
- enum nb_operation operation;
+ enum nb_cb_operation operation;
uint32_t seq;
const struct nb_node *nb_node;
const struct lyd_node *dnode;
@@ -703,7 +741,26 @@ struct nb_transaction {
struct nb_config {
struct lyd_node *dnode;
uint32_t version;
- struct nb_config_cbs cfg_chgs;
+};
+
+/*
+ * Northbound operations. The semantics of operations is explained in RFC 8072,
+ * section 2.5: https://datatracker.ietf.org/doc/html/rfc8072#section-2.5.
+ */
+enum nb_operation {
+ NB_OP_CREATE_EXCL, /* "create" */
+ NB_OP_CREATE, /* "merge" - kept for backward compatibility */
+ NB_OP_MODIFY, /* "merge" */
+ NB_OP_DESTROY, /* "remove" */
+ NB_OP_DELETE, /* "delete" */
+ NB_OP_REPLACE, /* "replace" */
+ NB_OP_MOVE, /* "move" */
+};
+
+struct nb_cfg_change {
+ char xpath[XPATH_MAXLEN];
+ enum nb_operation operation;
+ const char *value;
};
/* Callback function used by nb_oper_data_iterate(). */
@@ -711,12 +768,37 @@ typedef int (*nb_oper_data_cb)(const struct lysc_node *snode,
struct yang_translator *translator,
struct yang_data *data, void *arg);
+/**
+ * nb_oper_data_finish_cb() - finish a portion or all of a oper data walk.
+ * @tree - r/o copy of the tree created during this portion of the walk.
+ * @arg - finish arg passed to nb_op_iterate_yielding.
+ * @ret - NB_OK if done with walk, NB_YIELD if done with portion, otherwise an
+ * error.
+ *
+ * If nb_op_iterate_yielding() was passed with @should_batch set then this
+ * callback will be invoked during each portion (batch) of the walk.
+ *
+ * The @tree is read-only and should not be modified or freed.
+ *
+ * If this function returns anything but NB_OK then the walk will be terminated.
+ * and this function will not be called again regardless of if @ret was
+ * `NB_YIELD` or not.
+ *
+ * Return: NB_OK to continue or complete the walk normally, otherwise an error
+ * to immediately terminate the walk.
+ */
+/* Callback function used by nb_oper_data_iter_yielding(). */
+typedef enum nb_error (*nb_oper_data_finish_cb)(const struct lyd_node *tree,
+ void *arg, enum nb_error ret);
+
/* Iterate over direct child nodes only. */
#define NB_OPER_DATA_ITER_NORECURSE 0x0001
/* Hooks. */
DECLARE_HOOK(nb_notification_send, (const char *xpath, struct list *arguments),
(xpath, arguments));
+DECLARE_HOOK(nb_notification_tree_send,
+ (const char *xpath, const struct lyd_node *tree), (xpath, tree));
DECLARE_HOOK(nb_client_debug_config_write, (struct vty *vty), (vty));
DECLARE_HOOK(nb_client_debug_set_all, (uint32_t flags, bool set), (flags, set));
@@ -724,6 +806,7 @@ DECLARE_HOOK(nb_client_debug_set_all, (uint32_t flags, bool set), (flags, set));
extern struct debug nb_dbg_cbs_config;
extern struct debug nb_dbg_cbs_state;
extern struct debug nb_dbg_cbs_rpc;
+extern struct debug nb_dbg_cbs_notify;
extern struct debug nb_dbg_notif;
extern struct debug nb_dbg_events;
extern struct debug nb_dbg_libyang;
@@ -744,9 +827,16 @@ extern int nb_callback_get_keys(const struct nb_node *nb_node,
extern const void *nb_callback_lookup_entry(const struct nb_node *nb_node,
const void *parent_list_entry,
const struct yang_list_keys *keys);
+extern const void *nb_callback_lookup_node_entry(struct lyd_node *node,
+ const void *parent_list_entry);
+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,
char *errmsg, size_t errmsg_len);
+extern void nb_callback_notify(const struct nb_node *nb_node, const char *xpath,
+ struct lyd_node *dnode);
/*
* Create a northbound node for all YANG schema nodes.
@@ -769,6 +859,14 @@ void nb_nodes_delete(void);
*/
extern struct nb_node *nb_node_find(const char *xpath);
+/**
+ * nb_nodes_find() - find the NB nodes corresponding to complex xpath.
+ * @xpath: XPath to search for (with or without predicates).
+ *
+ * Return: a dynamic array (darr) of `struct nb_node *`s.
+ */
+extern struct nb_node **nb_nodes_find(const char *xpath);
+
extern void nb_node_set_dependency_cbs(const char *dependency_xpath,
const char *dependant_xpath,
struct nb_dependency_callbacks *cbs);
@@ -843,6 +941,32 @@ extern void nb_config_replace(struct nb_config *config_dst,
bool preserve_source);
/*
+ * Return a human-readable string representing a northbound operation.
+ *
+ * operation
+ * Northbound operation.
+ *
+ * Returns:
+ * String representation of the given northbound operation.
+ */
+extern const char *nb_operation_name(enum nb_operation operation);
+
+/*
+ * Validate if the northbound operation is allowed for the given node.
+ *
+ * nb_node
+ * Northbound node.
+ *
+ * operation
+ * Operation we want to check.
+ *
+ * Returns:
+ * true if the operation is allowed, false otherwise.
+ */
+extern bool nb_is_operation_allowed(struct nb_node *nb_node,
+ enum nb_operation oper);
+
+/*
* Edit a candidate configuration.
*
* candidate
@@ -866,7 +990,6 @@ extern void nb_config_replace(struct nb_config *config_dst,
*
* Returns:
* - NB_OK on success.
- * - NB_ERR_NOT_FOUND when the element to be deleted was not found.
* - NB_ERR for other errors.
*/
extern int nb_candidate_edit(struct nb_config *candidate,
@@ -917,11 +1040,8 @@ extern bool nb_candidate_needs_update(const struct nb_config *candidate);
* xpath_base
* Base xpath for config.
*
- * curr_xpath
- * Current xpath for config.
- *
- * xpath_index
- * Index of xpath being processed.
+ * in_backend
+ * Specify whether the changes are being applied in the backend or not.
*
* err_buf
* Buffer to store human-readable error message in case of error.
@@ -932,11 +1052,18 @@ extern bool nb_candidate_needs_update(const struct nb_config *candidate);
* error
* TRUE on error, FALSE on success
*/
-extern void nb_candidate_edit_config_changes(
- struct nb_config *candidate_config, struct nb_cfg_change cfg_changes[],
- size_t num_cfg_changes, const char *xpath_base, const char *curr_xpath,
- int xpath_index, char *err_buf, int err_bufsize, bool *error);
-
+extern void nb_candidate_edit_config_changes(struct nb_config *candidate_config,
+ struct nb_cfg_change cfg_changes[],
+ size_t num_cfg_changes,
+ const char *xpath_base,
+ bool in_backend, char *err_buf,
+ int err_bufsize, bool *error);
+
+
+extern void nb_config_diff_add_change(struct nb_config_cbs *changes,
+ enum nb_cb_operation operation,
+ uint32_t *seq,
+ const struct lyd_node *dnode);
/*
* Delete candidate configuration changes.
*
@@ -1258,7 +1385,7 @@ extern int nb_running_unlock(enum nb_client client, const void *user);
extern int nb_running_lock_check(enum nb_client client, const void *user);
/*
- * Iterate over operational data.
+ * Iterate over operational data -- deprecated.
*
* xpath
* Data path of the YANG data we want to iterate over.
@@ -1269,21 +1396,60 @@ extern int nb_running_lock_check(enum nb_client client, const void *user);
* flags
* NB_OPER_DATA_ITER_ flags to control how the iteration is performed.
*
+ * should_batch
+ * Should call finish cb with partial results (i.e., creating batches)
+ *
* cb
* Function to call with each data node.
*
* arg
* Arbitrary argument passed as the fourth parameter in each call to 'cb'.
*
+ * tree
+ * If non-NULL will contain the data tree built from the walk.
+ *
* Returns:
* NB_OK on success, NB_ERR otherwise.
*/
-extern int nb_oper_data_iterate(const char *xpath,
- struct yang_translator *translator,
- uint32_t flags, nb_oper_data_cb cb, void *arg);
+extern enum nb_error nb_oper_iterate_legacy(const char *xpath,
+ struct yang_translator *translator,
+ uint32_t flags, nb_oper_data_cb cb,
+ void *arg, struct lyd_node **tree);
+
+/**
+ * nb_oper_walk() - walk the schema building operational state.
+ * @xpath -
+ * @translator -
+ * @flags -
+ * @should_batch - should allow yielding and processing portions of the tree.
+ * @cb - callback invoked for each non-list, non-container node.
+ * @arg - arg to pass to @cb.
+ * @finish - function to call when done with portion or all of walk.
+ * @finish_arg - arg to pass to @finish.
+ *
+ * Return: walk - a cookie that can be used to cancel the walk.
+ */
+extern void *nb_oper_walk(const char *xpath, struct yang_translator *translator,
+ uint32_t flags, bool should_batch, nb_oper_data_cb cb,
+ void *arg, nb_oper_data_finish_cb finish,
+ void *finish_arg);
+
+/**
+ * nb_oper_cancel_walk() - cancel the in progress walk.
+ * @walk - value returned from nb_op_iterate_yielding()
+ *
+ * Should only be called on an in-progress walk. It is invalid to cancel and
+ * already finished walk. The walks `finish` callback will not be called.
+ */
+extern void nb_oper_cancel_walk(void *walk);
+
+/**
+ * nb_op_cancel_all_walks() - cancel all in progress walks.
+ */
+extern void nb_oper_cancel_all_walks(void);
/*
- * Validate if the northbound operation is valid for the given node.
+ * Validate if the northbound callback operation is valid for the given node.
*
* operation
* Operation we want to check.
@@ -1294,10 +1460,14 @@ extern int nb_oper_data_iterate(const char *xpath,
* Returns:
* true if the operation is valid, false otherwise.
*/
-extern bool nb_operation_is_valid(enum nb_operation operation,
- const struct lysc_node *snode);
+extern bool nb_cb_operation_is_valid(enum nb_cb_operation operation,
+ const struct lysc_node *snode);
/*
+ * DEPRECATED: This call and infra should no longer be used. Instead,
+ * the mgmtd supported tree based call `nb_notification_tree_send` should be
+ * used instead
+ *
* Send a YANG notification. This is a no-op unless the 'nb_notification_send'
* hook was registered by a northbound plugin.
*
@@ -1314,6 +1484,22 @@ extern bool nb_operation_is_valid(enum nb_operation operation,
extern int nb_notification_send(const char *xpath, struct list *arguments);
/*
+ * Send a YANG notification from a backend . This is a no-op unless th
+ * 'nb_notification_tree_send' hook was registered by a northbound plugin.
+ *
+ * xpath
+ * XPath of the YANG notification.
+ *
+ * tree
+ * The libyang tree for the notification.
+ *
+ * Returns:
+ * NB_OK on success, NB_ERR otherwise.
+ */
+extern int nb_notification_tree_send(const char *xpath,
+ const struct lyd_node *tree);
+
+/*
* Associate a user pointer to a configuration node.
*
* This should be called by northbound 'create' callbacks in the NB_EV_APPLY
@@ -1419,15 +1605,15 @@ extern void *nb_running_get_entry_non_rec(const struct lyd_node *dnode,
extern const char *nb_event_name(enum nb_event event);
/*
- * Return a human-readable string representing a northbound operation.
+ * Return a human-readable string representing a northbound callback operation.
*
* operation
- * Northbound operation.
+ * Northbound callback operation.
*
* Returns:
- * String representation of the given northbound operation.
+ * String representation of the given northbound callback operation.
*/
-extern const char *nb_operation_name(enum nb_operation operation);
+extern const char *nb_cb_operation_name(enum nb_cb_operation operation);
/*
* Return a human-readable string representing a northbound error.
@@ -1488,6 +1674,9 @@ extern void nb_init(struct event_loop *tm,
*/
extern void nb_terminate(void);
+extern void nb_oper_init(struct event_loop *loop);
+extern void nb_oper_terminate(void);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c
index c5582fc21c..8809ec2ad8 100644
--- a/lib/northbound_cli.c
+++ b/lib/northbound_cli.c
@@ -5,6 +5,7 @@
*/
#include <zebra.h>
+#include <sys/stat.h>
#include "libfrr.h"
#include "lib/version.h"
@@ -24,6 +25,7 @@
struct debug nb_dbg_cbs_config = {0, "Northbound callbacks: configuration"};
struct debug nb_dbg_cbs_state = {0, "Northbound callbacks: state"};
struct debug nb_dbg_cbs_rpc = {0, "Northbound callbacks: RPCs"};
+struct debug nb_dbg_cbs_notify = {0, "Northbound callbacks: notifications"};
struct debug nb_dbg_notif = {0, "Northbound notifications"};
struct debug nb_dbg_events = {0, "Northbound events"};
struct debug nb_dbg_libyang = {0, "libyang debugging"};
@@ -145,10 +147,9 @@ static int nb_cli_apply_changes_internal(struct vty *vty,
VTY_CHECK_XPATH;
- nb_candidate_edit_config_changes(
- vty->candidate_config, vty->cfg_changes, vty->num_cfg_changes,
- xpath_base, VTY_CURR_XPATH, vty->xpath_index, buf, sizeof(buf),
- &error);
+ nb_candidate_edit_config_changes(vty->candidate_config, vty->cfg_changes,
+ vty->num_cfg_changes, xpath_base,
+ false, buf, sizeof(buf), &error);
if (error) {
/*
* Failure to edit the candidate configuration should never
@@ -180,8 +181,26 @@ static int nb_cli_apply_changes_internal(struct vty *vty,
return CMD_SUCCESS;
}
+static void create_xpath_base_abs(struct vty *vty, char *xpath_base_abs,
+ size_t xpath_base_abs_size,
+ const char *xpath_base)
+{
+ memset(xpath_base_abs, 0, xpath_base_abs_size);
+
+ if (xpath_base[0] == 0)
+ xpath_base = ".";
+
+ /* If base xpath is relative, prepend current vty xpath. */
+ if (vty->xpath_index > 0 && xpath_base[0] == '.') {
+ strlcpy(xpath_base_abs, VTY_CURR_XPATH, xpath_base_abs_size);
+ xpath_base++; /* skip '.' */
+ }
+ strlcat(xpath_base_abs, xpath_base, xpath_base_abs_size);
+}
+
int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...)
{
+ char xpath_base_abs[XPATH_MAXLEN] = {};
char xpath_base[XPATH_MAXLEN] = {};
bool implicit_commit;
int ret;
@@ -195,22 +214,30 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...)
va_end(ap);
}
- if (vty_mgmt_fe_enabled()) {
+ create_xpath_base_abs(vty, xpath_base_abs, sizeof(xpath_base_abs),
+ xpath_base);
+
+ if (vty_mgmt_should_process_cli_apply_changes(vty)) {
VTY_CHECK_XPATH;
+ if (vty->type == VTY_FILE)
+ return CMD_SUCCESS;
+
implicit_commit = vty_needs_implicit_commit(vty);
- ret = vty_mgmt_send_config_data(vty);
+ ret = vty_mgmt_send_config_data(vty, xpath_base_abs,
+ implicit_commit);
if (ret >= 0 && !implicit_commit)
vty->mgmt_num_pending_setcfg++;
return ret;
}
- return nb_cli_apply_changes_internal(vty, xpath_base, false);
+ return nb_cli_apply_changes_internal(vty, xpath_base_abs, false);
}
int nb_cli_apply_changes_clear_pending(struct vty *vty,
const char *xpath_base_fmt, ...)
{
+ char xpath_base_abs[XPATH_MAXLEN] = {};
char xpath_base[XPATH_MAXLEN] = {};
bool implicit_commit;
int ret;
@@ -224,17 +251,28 @@ int nb_cli_apply_changes_clear_pending(struct vty *vty,
va_end(ap);
}
- if (vty_mgmt_fe_enabled()) {
- VTY_CHECK_XPATH;
+ create_xpath_base_abs(vty, xpath_base_abs, sizeof(xpath_base_abs),
+ xpath_base);
+ if (vty_mgmt_should_process_cli_apply_changes(vty)) {
+ VTY_CHECK_XPATH;
+ /*
+ * The legacy user wanted to clear pending (i.e., perform a
+ * commit immediately) due to some non-yang compatible
+ * functionality. This new mgmtd code however, continues to send
+ * changes putting off the commit until XFRR_end is received
+ * (i.e., end-of-config-file). This should be fine b/c all
+ * conversions to mgmtd require full proper implementations.
+ */
implicit_commit = vty_needs_implicit_commit(vty);
- ret = vty_mgmt_send_config_data(vty);
+ ret = vty_mgmt_send_config_data(vty, xpath_base_abs,
+ implicit_commit);
if (ret >= 0 && !implicit_commit)
vty->mgmt_num_pending_setcfg++;
return ret;
}
- return nb_cli_apply_changes_internal(vty, xpath_base, true);
+ return nb_cli_apply_changes_internal(vty, xpath_base_abs, true);
}
int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input,
@@ -753,7 +791,7 @@ DEFUN (config_exclusive,
"Configuration from vty interface\n"
"Configure exclusively from this terminal\n")
{
- return vty_config_enter(vty, true, true);
+ return vty_config_enter(vty, true, true, false);
}
/* Configure using a private candidate configuration. */
@@ -763,7 +801,7 @@ DEFUN (config_private,
"Configuration from vty interface\n"
"Configure using a private candidate configuration\n")
{
- return vty_config_enter(vty, true, false);
+ return vty_config_enter(vty, true, false, false);
}
DEFPY (config_commit,
@@ -1401,11 +1439,9 @@ static int nb_cli_oper_data_cb(const struct lysc_node *snode,
}
exit:
- yang_data_free(data);
return NB_OK;
error:
- yang_data_free(data);
return NB_ERR;
}
@@ -1454,9 +1490,14 @@ DEFPY (show_yang_operational_data,
ly_ctx = ly_native_ctx;
/* Obtain data. */
- dnode = yang_dnode_new(ly_ctx, false);
- ret = nb_oper_data_iterate(xpath, translator, 0, nb_cli_oper_data_cb,
- dnode);
+ if (translator) {
+ dnode = yang_dnode_new(ly_ctx, false);
+ ret = nb_oper_iterate_legacy(xpath, translator, 0,
+ nb_cli_oper_data_cb, dnode, NULL);
+ } else {
+ dnode = NULL;
+ ret = nb_oper_iterate_legacy(xpath, NULL, 0, NULL, NULL, &dnode);
+ }
if (ret != NB_OK) {
if (format == LYD_JSON)
vty_out(vty, "{}\n");
@@ -1464,7 +1505,8 @@ DEFPY (show_yang_operational_data,
/* embed ly_last_errmsg() when we get newer libyang */
vty_out(vty, "<!-- Not found -->\n");
}
- yang_dnode_free(dnode);
+ if (dnode)
+ yang_dnode_free(dnode);
return CMD_WARNING;
}
@@ -1731,13 +1773,15 @@ DEFPY (rollback_config,
/* Debug CLI commands. */
static struct debug *nb_debugs[] = {
&nb_dbg_cbs_config, &nb_dbg_cbs_state, &nb_dbg_cbs_rpc,
- &nb_dbg_notif, &nb_dbg_events, &nb_dbg_libyang,
+ &nb_dbg_cbs_notify, &nb_dbg_notif, &nb_dbg_events,
+ &nb_dbg_libyang,
};
static const char *const nb_debugs_conflines[] = {
"debug northbound callbacks configuration",
"debug northbound callbacks state",
"debug northbound callbacks rpc",
+ "debug northbound callbacks notify",
"debug northbound notifications",
"debug northbound events",
"debug northbound libyang",
@@ -1762,7 +1806,7 @@ DEFPY (debug_nb,
debug_nb_cmd,
"[no] debug northbound\
[<\
- callbacks$cbs [{configuration$cbs_cfg|state$cbs_state|rpc$cbs_rpc}]\
+ callbacks$cbs [{configuration$cbs_cfg|state$cbs_state|rpc$cbs_rpc|notify$cbs_notify}]\
|notifications$notifications\
|events$events\
|libyang$libyang\
@@ -1775,13 +1819,14 @@ DEFPY (debug_nb,
"State\n"
"RPC\n"
"Notifications\n"
+ "Notifications\n"
"Events\n"
"libyang debugging\n")
{
uint32_t mode = DEBUG_NODE2MODE(vty->node);
if (cbs) {
- bool none = (!cbs_cfg && !cbs_state && !cbs_rpc);
+ bool none = (!cbs_cfg && !cbs_state && !cbs_rpc && !cbs_notify);
if (none || cbs_cfg)
DEBUG_MODE_SET(&nb_dbg_cbs_config, mode, !no);
@@ -1789,6 +1834,8 @@ DEFPY (debug_nb,
DEBUG_MODE_SET(&nb_dbg_cbs_state, mode, !no);
if (none || cbs_rpc)
DEBUG_MODE_SET(&nb_dbg_cbs_rpc, mode, !no);
+ if (none || cbs_notify)
+ DEBUG_MODE_SET(&nb_dbg_cbs_notify, mode, !no);
}
if (notifications)
DEBUG_MODE_SET(&nb_dbg_notif, mode, !no);
diff --git a/lib/northbound_cli.h b/lib/northbound_cli.h
index c8f8a8481a..1a45794d45 100644
--- a/lib/northbound_cli.h
+++ b/lib/northbound_cli.h
@@ -32,7 +32,7 @@ extern struct nb_config *vty_shared_candidate_config;
* XPath (absolute or relative) of the configuration option being edited.
*
* operation
- * Operation to apply (either NB_OP_CREATE, NB_OP_MODIFY or NB_OP_DELETE).
+ * Operation to apply (either NB_OP_CREATE, NB_OP_MODIFY or NB_OP_DESTROY).
*
* value
* New value of the configuration option. Should be NULL for typeless YANG
diff --git a/lib/northbound_confd.c b/lib/northbound_confd.c
deleted file mode 100644
index 34406a110b..0000000000
--- 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_operation_is_valid(NB_OP_MODIFY, nb_node->snode))
- 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 6c33351cef..7957752589 100644
--- a/lib/northbound_grpc.cpp
+++ b/lib/northbound_grpc.cpp
@@ -427,25 +427,11 @@ static struct lyd_node *get_dnode_config(const std::string &path)
return dnode;
}
-static int get_oper_data_cb(const struct lysc_node *snode,
- struct yang_translator *translator,
- struct yang_data *data, void *arg)
-{
- struct lyd_node *dnode = static_cast<struct lyd_node *>(arg);
- int ret = yang_dnode_edit(dnode, data->xpath, data->value);
- yang_data_free(data);
-
- return (ret == 0) ? NB_OK : NB_ERR;
-}
-
static struct lyd_node *get_dnode_state(const std::string &path)
{
- struct lyd_node *dnode = yang_dnode_new(ly_native_ctx, false);
- if (nb_oper_data_iterate(path.c_str(), NULL, 0, get_oper_data_cb, dnode)
- != NB_OK) {
- yang_dnode_free(dnode);
- return NULL;
- }
+ struct lyd_node *dnode = NULL;
+
+ (void)nb_oper_iterate_legacy(path.c_str(), NULL, 0, NULL, NULL, &dnode);
return dnode;
}
diff --git a/lib/northbound_oper.c b/lib/northbound_oper.c
new file mode 100644
index 0000000000..2394b5e865
--- /dev/null
+++ b/lib/northbound_oper.c
@@ -0,0 +1,1857 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * October 14 2023, Christian Hopps <chopps@labn.net>
+ *
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ * Copyright (c) 2023, LabN Consulting, L.L.C.
+ *
+ */
+
+#include <zebra.h>
+#include "darr.h"
+#include "debug.h"
+#include "frrevent.h"
+#include "frrstr.h"
+#include "lib_errors.h"
+#include "monotime.h"
+#include "northbound.h"
+
+/*
+ * YANG model yielding design restrictions:
+ *
+ * In order to be able to yield and guarantee we have a valid data tree at the
+ * point of yielding we must know that each parent has all it's siblings
+ * collected to represent a complete element.
+ *
+ * Basically, there should be a only single branch in the schema tree that
+ * supports yielding. In practice this means:
+ *
+ * list node schema with lookup next:
+ * - must not have any lookup-next list node sibling schema
+ * - must not have any list or container node siblings with lookup-next descendants.
+ * - any parent list nodes must also be lookup-next list nodes
+ *
+ * We must also process containers with lookup-next descendants last.
+ */
+
+DEFINE_MTYPE_STATIC(LIB, NB_YIELD_STATE, "NB Yield State");
+DEFINE_MTYPE_STATIC(LIB, NB_NODE_INFOS, "NB Node Infos");
+
+/* Amount of time allowed to spend constructing oper-state prior to yielding */
+#define NB_OP_WALK_INTERVAL_MS 50
+#define NB_OP_WALK_INTERVAL_US (NB_OP_WALK_INTERVAL_MS * 1000)
+
+/* ---------- */
+/* Data Types */
+/* ---------- */
+PREDECL_LIST(nb_op_walks);
+
+/*
+ * This is our information about a node on the branch we are looking at
+ */
+struct nb_op_node_info {
+ struct lyd_node *inner;
+ const struct lysc_node *schema; /* inner schema in case we rm inner */
+ struct yang_list_keys keys; /* if list, keys to locate element */
+ const void *list_entry; /* opaque entry from user or NULL */
+ uint xpath_len; /* length of the xpath string for this node */
+ uint niters; /* # list elems create this iteration */
+ uint nents; /* # list elems create so far */
+ bool query_specific_entry : 1; /* this info is specific specified */
+ bool has_lookup_next : 1; /* if this node support lookup next */
+ bool lookup_next_ok : 1; /* if this and all previous support */
+};
+
+/**
+ * struct nb_op_yield_state - tracking required state for yielding.
+ *
+ * @xpath: current xpath representing the node_info stack.
+ * @xpath_orig: the original query string from the user
+ * @node_infos: the container stack for the walk from root to current
+ * @schema_path: the schema nodes along the path indicated by the query string.
+ * this will include the choice and case nodes which are not
+ * present in the query string.
+ * @query_tokstr: the query string tokenized with NUL bytes.
+ * @query_tokens: the string pointers to each query token (node).
+ * @non_specific_predicate: tracks if a query_token is non-specific predicate.
+ * @walk_root_level: The topmost specific node, +1 is where we start walking.
+ * @walk_start_level: @walk_root_level + 1.
+ * @query_base_level: the level the query string stops at and full walks
+ * commence below that.
+ */
+struct nb_op_yield_state {
+ /* Walking state */
+ char *xpath;
+ char *xpath_orig;
+ struct nb_op_node_info *node_infos;
+ const struct lysc_node **schema_path;
+ char *query_tokstr;
+ char **query_tokens;
+ uint8_t *non_specific_predicate;
+ int walk_root_level;
+ int walk_start_level;
+ int query_base_level;
+ bool query_list_entry; /* XXX query was for a specific list entry */
+
+ /* Yielding state */
+ bool query_did_entry; /* currently processing the entry */
+ bool should_batch;
+ struct timeval start_time;
+ struct yang_translator *translator;
+ uint32_t flags;
+ nb_oper_data_cb cb;
+ void *cb_arg;
+ nb_oper_data_finish_cb finish;
+ void *finish_arg;
+ struct event *walk_ev;
+ struct nb_op_walks_item link;
+};
+
+DECLARE_LIST(nb_op_walks, struct nb_op_yield_state, link);
+
+/* ---------------- */
+/* Global Variables */
+/* ---------------- */
+
+static struct event_loop *event_loop;
+static struct nb_op_walks_head nb_op_walks;
+
+/* --------------------- */
+/* Function Declarations */
+/* --------------------- */
+
+static enum nb_error nb_op_yield(struct nb_op_yield_state *ys);
+static struct lyd_node *ys_root_node(struct nb_op_yield_state *ys);
+
+/* -------------------- */
+/* Function Definitions */
+/* -------------------- */
+
+static inline struct nb_op_yield_state *
+nb_op_create_yield_state(const char *xpath, struct yang_translator *translator,
+ uint32_t flags, bool should_batch, nb_oper_data_cb cb,
+ void *cb_arg, nb_oper_data_finish_cb finish,
+ void *finish_arg)
+{
+ struct nb_op_yield_state *ys;
+
+ ys = XCALLOC(MTYPE_NB_YIELD_STATE, sizeof(*ys));
+ ys->xpath = darr_strdup_cap(xpath, (size_t)XPATH_MAXLEN);
+ ys->xpath_orig = darr_strdup(xpath);
+ ys->translator = translator;
+ ys->flags = flags;
+ ys->should_batch = should_batch;
+ ys->cb = cb;
+ ys->cb_arg = cb_arg;
+ ys->finish = finish;
+ ys->finish_arg = finish_arg;
+
+ nb_op_walks_add_tail(&nb_op_walks, ys);
+
+ return ys;
+}
+
+static inline void nb_op_free_yield_state(struct nb_op_yield_state *ys,
+ bool nofree_tree)
+{
+ if (ys) {
+ EVENT_OFF(ys->walk_ev);
+ nb_op_walks_del(&nb_op_walks, ys);
+ /* if we have a branch then free up it's libyang tree */
+ if (!nofree_tree && ys_root_node(ys))
+ lyd_free_all(ys_root_node(ys));
+ darr_free(ys->query_tokens);
+ darr_free(ys->non_specific_predicate);
+ darr_free(ys->query_tokstr);
+ darr_free(ys->schema_path);
+ darr_free(ys->node_infos);
+ darr_free(ys->xpath_orig);
+ darr_free(ys->xpath);
+ XFREE(MTYPE_NB_YIELD_STATE, ys);
+ }
+}
+
+static const struct lysc_node *ys_get_walk_stem_tip(struct nb_op_yield_state *ys)
+{
+ if (ys->walk_start_level <= 0)
+ return NULL;
+ return ys->node_infos[ys->walk_start_level - 1].schema;
+}
+
+static struct lyd_node *ys_root_node(struct nb_op_yield_state *ys)
+{
+ if (!darr_len(ys->node_infos))
+ return NULL;
+ return ys->node_infos[0].inner;
+}
+
+static void ys_trim_xpath(struct nb_op_yield_state *ys)
+{
+ uint len = darr_len(ys->node_infos);
+
+ if (len == 0)
+ darr_setlen(ys->xpath, 1);
+ else
+ darr_setlen(ys->xpath, darr_last(ys->node_infos)->xpath_len + 1);
+ ys->xpath[darr_len(ys->xpath) - 1] = 0;
+}
+
+static void ys_pop_inner(struct nb_op_yield_state *ys)
+{
+ uint len = darr_len(ys->node_infos);
+
+ assert(len);
+ darr_setlen(ys->node_infos, len - 1);
+ ys_trim_xpath(ys);
+}
+
+static void ys_free_inner(struct nb_op_yield_state *ys,
+ struct nb_op_node_info *ni)
+{
+ if (!CHECK_FLAG(ni->schema->nodetype, LYS_CASE | LYS_CHOICE))
+ lyd_free_tree(ni->inner);
+ ni->inner = NULL;
+}
+
+static void nb_op_get_keys(struct lyd_node_inner *list_node,
+ struct yang_list_keys *keys)
+{
+ struct lyd_node *child;
+ uint n = 0;
+
+ keys->num = 0;
+ LY_LIST_FOR (list_node->child, child) {
+ if (!lysc_is_key(child->schema))
+ break;
+ strlcpy(keys->key[n], yang_dnode_get_string(child, NULL),
+ sizeof(keys->key[n]));
+ n++;
+ }
+
+ keys->num = n;
+}
+
+/**
+ * __move_back_to_next() - move back to the next lookup-next schema
+ */
+static bool __move_back_to_next(struct nb_op_yield_state *ys, int i)
+{
+ struct nb_op_node_info *ni;
+ int j;
+
+ /*
+ * We will free the subtree we are trimming back to, or we will be done
+ * with the walk and will free the root on cleanup.
+ */
+
+ /* pop any node_info we dropped below on entry */
+ for (j = darr_ilen(ys->node_infos) - 1; j > i; j--)
+ ys_pop_inner(ys);
+
+ for (; i >= ys->walk_root_level; i--) {
+ if (ys->node_infos[i].has_lookup_next)
+ break;
+ ys_pop_inner(ys);
+ }
+
+ if (i < ys->walk_root_level)
+ return false;
+
+ ni = &ys->node_infos[i];
+
+ /*
+ * The i'th node has been lost after a yield so trim it from the tree
+ * now.
+ */
+ ys_free_inner(ys, ni);
+ ni->list_entry = NULL;
+
+ /*
+ * Leave the empty-of-data node_info on top, __walk will deal with
+ * this, by doing a lookup-next with the keys which we still have.
+ */
+
+ return true;
+}
+
+static void nb_op_resume_data_tree(struct nb_op_yield_state *ys)
+{
+ struct nb_op_node_info *ni;
+ struct nb_node *nn;
+ const void *parent_entry;
+ const void *list_entry;
+ uint i;
+
+ /*
+ * IMPORTANT: On yielding: we always yield during list iteration and
+ * after the initial list element has been created and handled, so the
+ * top of the yield stack will always point at a list node.
+ *
+ * Additionally, that list node has been processed and was in the
+ * process of being "get_next"d when we yielded. We process the
+ * lookup-next list node last so all the rest of the data (to the left)
+ * has been gotten. NOTE: To keep this simple we will require only a
+ * single lookup-next sibling in any parents list of children.
+ *
+ * Walk the rightmost branch (the node info stack) from base to tip
+ * verifying all list nodes are still present. If not we backup to the
+ * node which has a lookup next, and we prune the branch to this node.
+ * If the list node that went away is the topmost we will be using
+ * lookup_next, but if it's a parent then the list_entry will have been
+ * restored.
+ */
+ darr_foreach_i (ys->node_infos, i) {
+ ni = &ys->node_infos[i];
+ nn = ni->schema->priv;
+
+ if (!CHECK_FLAG(ni->schema->nodetype, LYS_LIST))
+ continue;
+
+ assert(ni->list_entry != NULL ||
+ ni == darr_last(ys->node_infos));
+
+ /* Verify the entry is still present */
+ parent_entry = (i == 0 ? NULL : ni[-1].list_entry);
+ list_entry = nb_callback_lookup_entry(nn, parent_entry,
+ &ni->keys);
+ if (!list_entry || list_entry != ni->list_entry) {
+ /* May be NULL or a different pointer
+ * move back to first of
+ * container with last lookup_next list node
+ * (which may be this one) and get next.
+ */
+ if (!__move_back_to_next(ys, i))
+ DEBUGD(&nb_dbg_events,
+ "%s: Nothing to resume after delete during walk (yield)",
+ __func__);
+ return;
+ }
+ }
+}
+
+/*
+ * Can only yield if all list nodes to root have lookup_next() callbacks
+ *
+ * In order to support lookup_next() the list_node get_next() callback
+ * needs to return ordered (i.e., sorted) results.
+ */
+
+/* ======================= */
+/* Start of walk init code */
+/* ======================= */
+
+/**
+ * __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)
+ *
+ * Use the longest prefix of @xpath_in as possible to resolve to a tree (trunk).
+ * This is logically as if we walked along the xpath string resolving each
+ * nodename reference (in particular list nodes) until we could not.
+ *
+ * Return: error if any, if no error then @dnode contains the tree (trunk).
+ */
+static enum nb_error nb_op_xpath_to_trunk(const char *xpath_in,
+ struct lyd_node **trunk)
+{
+ char *xpath = NULL;
+ enum nb_error ret = NB_OK;
+ LY_ERR err;
+
+ darr_in_strdup(xpath, xpath_in);
+ for (;;) {
+ err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0,
+ LYD_NEW_PATH_UPDATE, NULL, trunk);
+ if (err == LY_SUCCESS)
+ break;
+
+ ret = __xpath_pop_node(xpath);
+ if (ret != NB_OK)
+ break;
+ }
+ darr_free(xpath);
+ return ret;
+}
+
+/*
+ * Finish initializing the node info based on the xpath string, and previous
+ * node_infos on the stack. If this node is a list node, obtain the specific
+ * list-entry object.
+ */
+static enum nb_error nb_op_ys_finalize_node_info(struct nb_op_yield_state *ys,
+ uint index)
+{
+ struct nb_op_node_info *ni = &ys->node_infos[index];
+ struct lyd_node *inner = ni->inner;
+ struct nb_node *nn = ni->schema->priv;
+ bool yield_ok = ys->finish != NULL;
+
+ ni->has_lookup_next = nn->cbs.lookup_next != NULL;
+
+ /* track the last list_entry until updated by new list node */
+ ni->list_entry = index == 0 ? NULL : ni[-1].list_entry;
+
+ /* Assert that we are walking the rightmost branch */
+ assert(!inner->parent || inner == inner->parent->child->prev);
+
+ if (CHECK_FLAG(inner->schema->nodetype,
+ LYS_CASE | LYS_CHOICE | LYS_CONTAINER)) {
+ /* containers have only zero or one child on a branch of a tree */
+ inner = ((struct lyd_node_inner *)inner)->child;
+ assert(!inner || inner->prev == inner);
+ ni->lookup_next_ok = yield_ok &&
+ (index == 0 || ni[-1].lookup_next_ok);
+ return NB_OK;
+ }
+
+ assert(CHECK_FLAG(inner->schema->nodetype, LYS_LIST));
+
+ ni->lookup_next_ok = yield_ok && ni->has_lookup_next &&
+ (index == 0 || ni[-1].lookup_next_ok);
+
+ nb_op_get_keys((struct lyd_node_inner *)inner, &ni->keys);
+
+ /* A list entry cannot be present in a tree w/o it's keys */
+ assert(ni->keys.num == yang_snode_num_keys(inner->schema));
+
+ /*
+ * Get this nodes opaque list_entry object
+ */
+
+ if (!nn->cbs.lookup_entry) {
+ flog_warn(EC_LIB_NB_OPERATIONAL_DATA,
+ "%s: data path doesn't support iteration over operational data: %s",
+ __func__, ys->xpath);
+ return NB_ERR_NOT_FOUND;
+ }
+
+ /* ni->list_entry starts as the parent entry of this node */
+ ni->list_entry = nb_callback_lookup_entry(nn, ni->list_entry, &ni->keys);
+ if (ni->list_entry == NULL) {
+ flog_warn(EC_LIB_NB_OPERATIONAL_DATA,
+ "%s: list entry lookup failed", __func__);
+ return NB_ERR_NOT_FOUND;
+ }
+
+ /*
+ * By definition any list element we can get a specific list_entry for
+ * is specific.
+ */
+ ni->query_specific_entry = true;
+
+ return NB_OK;
+}
+
+/**
+ * nb_op_ys_init_node_infos() - initialize the node info stack from the query.
+ * @ys: the yield state for this tree walk.
+ *
+ * On starting a walk we initialize the node_info stack as deeply as possible
+ * based on specific node references in the query string. We will stop at the
+ * point in the query string that is not specific (e.g., a list element without
+ * it's keys predicate)
+ *
+ * Return: northbound return value (enum nb_error)
+ */
+static enum nb_error nb_op_ys_init_node_infos(struct nb_op_yield_state *ys)
+{
+ struct nb_op_node_info *ni;
+ struct lyd_node *inner;
+ struct lyd_node *node = NULL;
+ enum nb_error ret;
+ uint i, len;
+ char *tmp;
+
+ /*
+ * Obtain the trunk of the data node tree of the query.
+ *
+ * These are the nodes from the root that could be specifically
+ * identified with the query string. The trunk ends when a no specific
+ * node could be identified (e.g., a list-node name with no keys).
+ */
+
+ ret = nb_op_xpath_to_trunk(ys->xpath, &node);
+ if (ret || !node) {
+ flog_warn(EC_LIB_LIBYANG,
+ "%s: can't instantiate concrete path using xpath: %s",
+ __func__, ys->xpath);
+ if (!ret)
+ ret = NB_ERR_NOT_FOUND;
+ return ret;
+ }
+
+ /* Move up to the container if on a leaf currently. */
+ if (node &&
+ !CHECK_FLAG(node->schema->nodetype, LYS_CONTAINER | LYS_LIST)) {
+ struct lyd_node *leaf = node;
+
+ node = &node->parent->node;
+
+ /*
+ * If the leaf is not a key, delete it, because it has a wrong
+ * empty value.
+ */
+ if (!lysc_is_key(leaf->schema))
+ lyd_free_tree(leaf);
+ }
+ assert(!node ||
+ CHECK_FLAG(node->schema->nodetype, LYS_CONTAINER | LYS_LIST));
+ if (!node)
+ return NB_ERR_NOT_FOUND;
+
+ inner = node;
+ for (len = 1; inner->parent; len++)
+ inner = &inner->parent->node;
+
+ darr_append_nz_mt(ys->node_infos, len, MTYPE_NB_NODE_INFOS);
+
+ /*
+ * For each node find the prefix of the xpath query that identified it
+ * -- save the prefix length.
+ */
+ inner = node;
+ for (i = len; i > 0; i--, inner = &inner->parent->node) {
+ ni = &ys->node_infos[i - 1];
+ ni->inner = inner;
+ ni->schema = inner->schema;
+ /*
+ * NOTE: we could build this by hand with a litte more effort,
+ * but this simple implementation works and won't be expensive
+ * since the number of nodes is small and only done once per
+ * query.
+ */
+ tmp = yang_dnode_get_path(inner, NULL, 0);
+ ni->xpath_len = strlen(tmp);
+
+ /* Replace users supplied xpath with the libyang returned value */
+ if (i == len)
+ darr_in_strdup(ys->xpath, tmp);
+
+ /* The prefix must match the prefix of the stored xpath */
+ assert(!strncmp(tmp, ys->xpath, ni->xpath_len));
+ free(tmp);
+ }
+
+ /*
+ * Obtain the specific list-entry objects for each list node on the
+ * trunk and finish initializing the node_info structs.
+ */
+
+ darr_foreach_i (ys->node_infos, i) {
+ ret = nb_op_ys_finalize_node_info(ys, i);
+ if (ret != NB_OK) {
+ if (ys->node_infos[0].inner)
+ lyd_free_all(ys->node_infos[0].inner);
+ darr_free(ys->node_infos);
+ return ret;
+ }
+ }
+
+ ys->walk_start_level = darr_len(ys->node_infos);
+
+ ys->walk_root_level = (int)ys->walk_start_level - 1;
+
+ return NB_OK;
+}
+
+/* ================ */
+/* End of init code */
+/* ================ */
+
+/**
+ * nb_op_add_leaf() - Add leaf data to the get tree results
+ * @ys - the yield state for this tree walk.
+ * @nb_node - the northbound node representing this leaf.
+ * @xpath - the xpath (with key predicates) to this leaf value.
+ *
+ * Return: northbound return value (enum nb_error)
+ */
+static enum nb_error nb_op_iter_leaf(struct nb_op_yield_state *ys,
+ const struct nb_node *nb_node,
+ const char *xpath)
+{
+ const struct lysc_node *snode = nb_node->snode;
+ struct nb_op_node_info *ni = darr_last(ys->node_infos);
+ struct yang_data *data;
+ enum nb_error ret = NB_OK;
+ LY_ERR err;
+
+ if (CHECK_FLAG(snode->flags, LYS_CONFIG_W))
+ return NB_OK;
+
+ /* Ignore list keys. */
+ if (lysc_is_key(snode))
+ return NB_OK;
+
+ data = nb_callback_get_elem(nb_node, xpath, ni->list_entry);
+ if (data == NULL)
+ return NB_OK;
+
+ /* Add a dnode to our tree */
+ err = lyd_new_term(ni->inner, snode->module, snode->name, data->value,
+ false, NULL);
+ if (err) {
+ yang_data_free(data);
+ return NB_ERR_RESOURCE;
+ }
+
+ if (ys->cb)
+ ret = (*ys->cb)(nb_node->snode, ys->translator, data,
+ ys->cb_arg);
+ yang_data_free(data);
+
+ return ret;
+}
+
+static enum nb_error nb_op_iter_leaflist(struct nb_op_yield_state *ys,
+ const struct nb_node *nb_node,
+ const char *xpath)
+{
+ const struct lysc_node *snode = nb_node->snode;
+ struct nb_op_node_info *ni = darr_last(ys->node_infos);
+ const void *list_entry = NULL;
+ enum nb_error ret = NB_OK;
+ LY_ERR err;
+
+ if (CHECK_FLAG(snode->flags, LYS_CONFIG_W))
+ return NB_OK;
+
+ do {
+ struct yang_data *data;
+
+ list_entry = nb_callback_get_next(nb_node, ni->list_entry,
+ list_entry);
+ if (!list_entry)
+ /* End of the list. */
+ break;
+
+ data = nb_callback_get_elem(nb_node, xpath, list_entry);
+ if (data == NULL)
+ continue;
+
+ /* Add a dnode to our tree */
+ err = lyd_new_term(ni->inner, snode->module, snode->name,
+ data->value, false, NULL);
+ if (err) {
+ yang_data_free(data);
+ return NB_ERR_RESOURCE;
+ }
+
+ if (ys->cb)
+ ret = (*ys->cb)(nb_node->snode, ys->translator, data,
+ ys->cb_arg);
+ yang_data_free(data);
+ } while (ret == NB_OK && list_entry);
+
+ return ret;
+}
+
+
+static bool nb_op_schema_path_has_predicate(struct nb_op_yield_state *ys,
+ int level)
+{
+ if (level > darr_lasti(ys->query_tokens))
+ return false;
+ return strchr(ys->query_tokens[level], '[') != NULL;
+}
+
+/**
+ * nb_op_empty_container_ok() - determine if should keep empty container node.
+ *
+ * Return: true if the empty container should be kept.
+ */
+static bool nb_op_empty_container_ok(const struct lysc_node *snode,
+ const char *xpath, const void *list_entry)
+{
+ struct nb_node *nn = snode->priv;
+ struct yang_data *data;
+
+ if (!CHECK_FLAG(snode->flags, LYS_PRESENCE))
+ return false;
+
+ if (!nn->cbs.get_elem)
+ return false;
+
+ data = nb_callback_get_elem(nn, xpath, list_entry);
+ if (data) {
+ yang_data_free(data);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * nb_op_get_child_path() - add child node name to the xpath.
+ * @xpath_parent - a darr string for the parent node.
+ * @schild - the child schema node.
+ * @xpath_child - a previous return value from this function to reuse.
+ */
+static char *nb_op_get_child_path(const char *xpath_parent,
+ const struct lysc_node *schild,
+ char *xpath_child)
+{
+ /* "/childname" */
+ uint space, extra = strlen(schild->name) + 1;
+ bool new_mod = (!schild->parent ||
+ schild->parent->module != schild->module);
+ int n;
+
+ if (new_mod)
+ /* "modulename:" */
+ extra += strlen(schild->module->name) + 1;
+ space = darr_len(xpath_parent) + extra;
+
+ if (xpath_parent == xpath_child)
+ darr_ensure_cap(xpath_child, space);
+ else
+ darr_in_strdup_cap(xpath_child, xpath_parent, space);
+ if (new_mod)
+ n = snprintf(darr_strnul(xpath_child), extra + 1, "/%s:%s",
+ schild->module->name, schild->name);
+ else
+ n = snprintf(darr_strnul(xpath_child), extra + 1, "/%s",
+ schild->name);
+ assert(n == (int)extra);
+ _darr_len(xpath_child) += extra;
+ return xpath_child;
+}
+
+static bool __is_yielding_node(const struct lysc_node *snode)
+{
+ struct nb_node *nn = snode->priv;
+
+ return nn->cbs.lookup_next != NULL;
+}
+
+static const struct lysc_node *__sib_next(bool yn, const struct lysc_node *sib)
+{
+ for (; sib; sib = sib->next) {
+ /* Always skip keys. */
+ if (lysc_is_key(sib))
+ continue;
+ if (yn == __is_yielding_node(sib))
+ return sib;
+ }
+ return NULL;
+}
+
+/**
+ * nb_op_sib_next() - Return the next sibling to walk to
+ * @ys: the yield state for this tree walk.
+ * @sib: the currently being visited sibling
+ *
+ * Return: the next sibling to walk to, walking non-yielding before yielding.
+ */
+static const struct lysc_node *nb_op_sib_next(struct nb_op_yield_state *ys,
+ const struct lysc_node *sib)
+{
+ struct lysc_node *parent = sib->parent;
+ bool yn = __is_yielding_node(sib);
+
+ /*
+ * If the node info stack is shorter than the schema path then we are
+ * doign specific query still on the node from the schema path (should
+ * match) so just return NULL (i.e., don't process siblings)
+ */
+ if (darr_len(ys->schema_path) > darr_len(ys->node_infos))
+ return NULL;
+ /*
+ * If sib is on top of the node info stack then
+ * 1) it's a container node -or-
+ * 2) it's a list node that we were walking and we've reach the last entry
+ * 3) if sib is a list and the list was empty we never would have
+ * pushed sib on the stack so the top of the stack is the parent
+ *
+ * If the query string included this node then we do not process any
+ * siblings as we are not walking all the parent's children just this
+ * specified one give by the query string.
+ */
+ if (sib == darr_last(ys->node_infos)->schema &&
+ darr_len(ys->schema_path) >= darr_len(ys->node_infos))
+ return NULL;
+ /* case (3) */
+ else if (sib->nodetype == LYS_LIST &&
+ parent == darr_last(ys->node_infos)->schema &&
+ darr_len(ys->schema_path) > darr_len(ys->node_infos))
+ return NULL;
+
+ sib = __sib_next(yn, sib->next);
+ if (sib)
+ return sib;
+ if (yn)
+ return NULL;
+ return __sib_next(true, lysc_node_child(parent));
+}
+/*
+ * sib_walk((struct lyd_node *)ni->inner->node.parent->parent->parent->parent->parent->parent->parent)
+ */
+
+/**
+ * nb_op_sib_first() - obtain the first child to walk to
+ * @ys: the yield state for this tree walk.
+ * @parent: the parent whose child we seek
+ * @skip_keys: if should skip over keys
+ *
+ * Return: the first child to continue the walk to, starting with non-yielding
+ * siblings then yielding ones. There should be no more than 1 yielding sibling.
+ */
+static const struct lysc_node *nb_op_sib_first(struct nb_op_yield_state *ys,
+ const struct lysc_node *parent)
+{
+ const struct lysc_node *sib = lysc_node_child(parent);
+ const struct lysc_node *first_sib;
+
+ /*
+ * NOTE: when we want to handle root level walks we will need to use
+ * lys_getnext() to walk root level of each module and
+ * ly_ctx_get_module_iter() to walk the modules.
+ */
+ assert(darr_len(ys->node_infos) > 0);
+
+ /*
+ * The top of the node stack points at @parent.
+ *
+ * If the schema path (original query) is longer than our current node
+ * info stack (current xpath location), we are building back up to the
+ * base of the user query, return the next schema node from the query
+ * string (schema_path).
+ */
+ if (darr_last(ys->node_infos) != NULL &&
+ !CHECK_FLAG(darr_last(ys->node_infos)->schema->nodetype,
+ LYS_CASE | LYS_CHOICE))
+ assert(darr_last(ys->node_infos)->schema == parent);
+ if (darr_lasti(ys->node_infos) < ys->query_base_level)
+ return ys->schema_path[darr_lasti(ys->node_infos) + 1];
+
+ /* We always skip keys. */
+ while (sib && lysc_is_key(sib))
+ sib = sib->next;
+ if (!sib)
+ return NULL;
+
+ /* Return non-yielding node's first */
+ first_sib = sib;
+ if (__is_yielding_node(sib)) {
+ sib = __sib_next(false, sib);
+ if (sib)
+ return sib;
+ }
+ return first_sib;
+}
+
+/*
+ * "3-dimensional" walk from base of the tree to the tip in-order.
+ *
+ * The actual tree is only 2-dimensional as list nodes are organized as adjacent
+ * siblings under a common parent perhaps with other siblings to each side;
+ * however, using 3d view here is easier to diagram.
+ *
+ * - A list node is yielding if it has a lookup_next callback.
+ * - All other node types are not yielding.
+ * - There's only one yielding node in a list of children (i.e., siblings).
+ *
+ * We visit all non-yielding children prior to the yielding child.
+ * That way we have the fullest tree possible even when something is deleted
+ * during a yield.
+ * --- child/parent descendant poinilnters
+ * ... next/prev sibling pointers
+ * o.o list entries pointers
+ * ~~~ diagram extension connector
+ * 1
+ * / \
+ * / \ o~~~~12
+ * / \ . / \
+ * 2.......5 o~~~9 13...14
+ * / \ | . / \
+ * 3...4 6 10...11 Cont Nodes: 1,2,5
+ * / \ List Nodes: 6,9,12
+ * 7...8 Leaf Nodes: 3,4,7,8,10,11,13,14
+ * Schema Leaf A: 3
+ * Schema Leaf B: 4
+ * Schema Leaf C: 7,10,13
+ * Schema Leaf D: 8,11,14
+ */
+static enum nb_error __walk(struct nb_op_yield_state *ys, bool is_resume)
+{
+ const struct lysc_node *walk_stem_tip = ys_get_walk_stem_tip(ys);
+ const struct lysc_node *sib;
+ const void *parent_list_entry = NULL;
+ const void *list_entry = NULL;
+ struct nb_op_node_info *ni, *pni;
+ struct lyd_node *node;
+ struct nb_node *nn;
+ char *xpath_child = NULL;
+ // bool at_query_base;
+ bool at_root_level, list_start, is_specific_node;
+ enum nb_error ret = NB_OK;
+ LY_ERR err;
+ int at_clevel;
+ uint len;
+
+
+ monotime(&ys->start_time);
+
+ /* Don't currently support walking all root nodes */
+ if (!walk_stem_tip)
+ return NB_ERR_NOT_FOUND;
+
+ if (ys->schema_path[0]->nodetype == LYS_CHOICE) {
+ flog_err(EC_LIB_NB_OPERATIONAL_DATA,
+ "%s: unable to walk root level choice node from module: %s",
+ __func__, ys->schema_path[0]->module->name);
+ return NB_ERR;
+ }
+
+ /*
+ * If we are resuming then start with the list container on top.
+ * Otherwise get the first child of the container we are walking,
+ * starting with non-yielding children.
+ */
+ if (is_resume)
+ sib = darr_last(ys->node_infos)->schema;
+ else {
+ /*
+ * Start with non-yielding children first.
+ *
+ * When adding root level walks, the sibling list are the root
+ * level nodes of all modules
+ */
+ sib = nb_op_sib_first(ys, walk_stem_tip);
+ if (!sib)
+ return NB_ERR_NOT_FOUND;
+ }
+
+
+ while (true) {
+ /* Grab the top container/list node info on the stack */
+ at_clevel = darr_lasti(ys->node_infos);
+ ni = &ys->node_infos[at_clevel];
+
+ /*
+ * This is the level of the last specific node at init
+ * time. +1 would be the first non-specific list or
+ * non-container if present in the container node.
+ */
+ at_root_level = at_clevel == ys->walk_root_level;
+
+ if (!sib) {
+ /*
+ * We've reached the end of the siblings inside a
+ * containing node; either a container, case, choice, or
+ * a specific list node entry.
+ *
+ * We handle case/choice/container node inline; however,
+ * for lists we are only done with a specific entry and
+ * need to move to the next element on the list so we
+ * drop down into the switch for that case.
+ */
+
+ /* Grab the containing node. */
+ sib = ni->schema;
+
+ if (CHECK_FLAG(sib->nodetype,
+ LYS_CASE | LYS_CHOICE | LYS_CONTAINER)) {
+ /* If we added an empty container node (no
+ * children) and it's not a presence container
+ * or it's not backed by the get_elem callback,
+ * remove the node from the tree.
+ */
+ if (sib->nodetype == LYS_CONTAINER &&
+ !lyd_child(ni->inner) &&
+ !nb_op_empty_container_ok(sib, ys->xpath,
+ ni->list_entry))
+ ys_free_inner(ys, ni);
+
+ /* If we have returned to our original walk base,
+ * then we are done with the walk.
+ */
+ if (at_root_level) {
+ ret = NB_OK;
+ goto done;
+ }
+ /*
+ * Grab the sibling of the container we are
+ * about to pop, so we will be mid-walk on the
+ * parent containers children.
+ */
+ sib = nb_op_sib_next(ys, sib);
+
+ /* Pop container node to the parent container */
+ ys_pop_inner(ys);
+
+ /*
+ * If are were working on a user narrowed path
+ * then we are done with these siblings.
+ */
+ if (darr_len(ys->schema_path) >
+ darr_len(ys->node_infos))
+ sib = NULL;
+
+ /* Start over */
+ continue;
+ }
+ /*
+ * If we are here we have reached the end of the
+ * children of a list entry node. sib points
+ * at the list node info.
+ */
+ }
+
+ if (CHECK_FLAG(sib->nodetype,
+ LYS_LEAF | LYS_LEAFLIST | LYS_CONTAINER))
+ xpath_child = nb_op_get_child_path(ys->xpath, sib,
+ xpath_child);
+ else if (CHECK_FLAG(sib->nodetype, LYS_CASE | LYS_CHOICE))
+ darr_in_strdup(xpath_child, ys->xpath);
+
+ nn = sib->priv;
+
+ switch (sib->nodetype) {
+ case LYS_LEAF:
+ /*
+ * If we have a non-specific walk to a specific leaf
+ * (e.g., "..../route-entry/metric") and the leaf value
+ * is not present, then we are left with the data nodes
+ * of the stem of the branch to the missing leaf data.
+ * For containers this will get cleaned up by the
+ * container code above that looks for no children;
+ * however, this doesn't work for lists.
+ *
+ * (FN:A) We need a similar check for empty list
+ * elements. Empty list elements below the
+ * query_base_level (i.e., the schema path length)
+ * should be cleaned up as they don't support anything
+ * the user is querying for, if they are above the
+ * query_base_level then they are part of the walk and
+ * should be kept.
+ */
+ ret = nb_op_iter_leaf(ys, nn, xpath_child);
+ if (ret != NB_OK)
+ goto done;
+ sib = nb_op_sib_next(ys, sib);
+ continue;
+ case LYS_LEAFLIST:
+ ret = nb_op_iter_leaflist(ys, nn, xpath_child);
+ if (ret != NB_OK)
+ goto done;
+ sib = nb_op_sib_next(ys, sib);
+ continue;
+ case LYS_CASE:
+ case LYS_CHOICE:
+ case LYS_CONTAINER:
+ if (CHECK_FLAG(nn->flags, F_NB_NODE_CONFIG_ONLY)) {
+ sib = nb_op_sib_next(ys, sib);
+ continue;
+ }
+
+ if (sib->nodetype != LYS_CONTAINER) {
+ /* Case/choice use parent inner. */
+ /* TODO: thus we don't support root level choice */
+ node = ni->inner;
+ } else {
+ err = lyd_new_inner(ni->inner, sib->module,
+ sib->name, false, &node);
+ if (err) {
+ ret = NB_ERR_RESOURCE;
+ goto done;
+ }
+ }
+
+ /* push this choice/container node on top of the stack */
+ ni = darr_appendz(ys->node_infos);
+ ni->inner = node;
+ ni->schema = sib;
+ ni->lookup_next_ok = ni[-1].lookup_next_ok;
+ ni->list_entry = ni[-1].list_entry;
+
+ darr_in_strdup(ys->xpath, xpath_child);
+ ni->xpath_len = darr_strlen(ys->xpath);
+
+ sib = nb_op_sib_first(ys, sib);
+ continue;
+ case LYS_LIST:
+
+ /*
+ * Notes:
+ *
+ * NOTE: ni->inner may be NULL here if we resumed and it
+ * was gone. ni->schema and ni->keys will still be
+ * valid.
+ *
+ * NOTE: At this point sib is never NULL; however, if it
+ * was NULL at the top of the loop, then we were done
+ * working on a list element's children and will be
+ * attempting to get the next list element here so sib
+ * == ni->schema (i.e., !list_start).
+ *
+ * (FN:A): Before doing this let's remove empty list
+ * elements that are "inside" the query string as they
+ * represent a stem which didn't lead to actual data
+ * being requested by the user -- for example,
+ * ".../route-entry/metric" if metric is not present we
+ * don't want to return an empty route-entry to the
+ * user.
+ */
+
+ node = NULL;
+ list_start = ni->schema != sib;
+ if (list_start) {
+ /*
+ * List iteration: First Element
+ * -----------------------------
+ *
+ * Our node info wasn't on top (wasn't an entry
+ * for sib) so this is a new list iteration, we
+ * will push our node info below. The top is our
+ * parent.
+ */
+ if (CHECK_FLAG(nn->flags,
+ F_NB_NODE_CONFIG_ONLY)) {
+ sib = nb_op_sib_next(ys, sib);
+ continue;
+ }
+ /* we are now at one level higher */
+ at_clevel += 1;
+ pni = ni;
+ ni = NULL;
+ } else {
+ /*
+ * List iteration: Next Element
+ * ----------------------------
+ *
+ * This is the case where `sib == NULL` at the
+ * top of the loop, so, we just completed the
+ * walking the children of a list entry, i.e.,
+ * we are done with that list entry.
+ *
+ * `sib` was reset to point at the our list node
+ * at the top of node_infos.
+ *
+ * Within this node_info, `ys->xpath`, `inner`,
+ * `list_entry`, and `xpath_len` are for the
+ * previous list entry, and need to be updated.
+ */
+ pni = darr_len(ys->node_infos) > 1 ? &ni[-1]
+ : NULL;
+ }
+
+ parent_list_entry = pni ? pni->list_entry : NULL;
+ list_entry = ni ? ni->list_entry : NULL;
+
+ /*
+ * Before yielding we check to see if we are doing a
+ * specific list entry instead of a full list iteration.
+ * We do not want to yield during specific list entry
+ * processing.
+ */
+
+ /*
+ * If we are at a list start check to see if the node
+ * has a predicate. If so we will try and fetch the data
+ * node now that we've built part of the tree, if the
+ * predicates are keys or only depend on the tree already
+ * built, it should create the element for us.
+ */
+ is_specific_node = false;
+ if (list_start &&
+ at_clevel <= darr_lasti(ys->query_tokens) &&
+ !ys->non_specific_predicate[at_clevel] &&
+ nb_op_schema_path_has_predicate(ys, at_clevel)) {
+ err = lyd_new_path(pni->inner, NULL,
+ ys->query_tokens[at_clevel],
+ NULL, 0, &node);
+ if (!err)
+ is_specific_node = true;
+ else if (err == LY_EVALID)
+ ys->non_specific_predicate[at_clevel] = true;
+ else {
+ flog_err(EC_LIB_NB_OPERATIONAL_DATA,
+ "%s: unable to create node for specific query string: %s: %s",
+ __func__,
+ ys->query_tokens[at_clevel],
+ yang_ly_strerrcode(err));
+ ret = NB_ERR;
+ goto done;
+ }
+ }
+
+ if (list_entry && ni->query_specific_entry) {
+ /*
+ * Ending specific list entry processing.
+ */
+ assert(!list_start);
+ is_specific_node = true;
+ list_entry = NULL;
+ }
+
+ /*
+ * Should we yield?
+ *
+ * Don't yield if we have a specific entry.
+ */
+ if (!is_specific_node && ni && ni->lookup_next_ok &&
+ // make sure we advance, if the interval is
+ // fast and we are very slow.
+ ((monotime_since(&ys->start_time, NULL) >
+ NB_OP_WALK_INTERVAL_US &&
+ ni->niters) ||
+ (ni->niters + 1) % 10000 == 0)) {
+ /* This is a yield supporting list node and
+ * we've been running at least our yield
+ * interval, so yield.
+ *
+ * NOTE: we never yield on list_start, and we
+ * are always about to be doing a get_next.
+ */
+ DEBUGD(&nb_dbg_events,
+ "%s: yielding after %u iterations",
+ __func__, ni->niters);
+
+ ni->niters = 0;
+ ret = NB_YIELD;
+ goto done;
+ }
+
+ /*
+ * Now get the backend list_entry opaque object for
+ * this list entry from the backend.
+ */
+
+ if (is_specific_node) {
+ /*
+ * Specific List Entry:
+ * --------------------
+ */
+ if (list_start) {
+ list_entry =
+ nb_callback_lookup_node_entry(
+ node, parent_list_entry);
+ /*
+ * If the node we created from a
+ * specific predicate entry is not
+ * actually there we need to delete the
+ * node from our data tree
+ */
+ if (!list_entry) {
+ lyd_free_tree(node);
+ node = NULL;
+ }
+ }
+ } else if (!list_start && !list_entry &&
+ ni->has_lookup_next) {
+ /*
+ * After Yield:
+ * ------------
+ * After a yield the list_entry may have become
+ * invalid, so use lookup_next callback with
+ * parent and keys instead to find next element.
+ */
+ list_entry =
+ nb_callback_lookup_next(nn,
+ parent_list_entry,
+ &ni->keys);
+ } else {
+ /*
+ * Normal List Iteration:
+ * ----------------------
+ * Start (list_entry == NULL) or continue
+ * (list_entry != NULL) the list iteration.
+ */
+ /* Obtain [next] list entry. */
+ list_entry =
+ nb_callback_get_next(nn,
+ parent_list_entry,
+ list_entry);
+ }
+
+ /*
+ * (FN:A) Reap empty list element? Check to see if we
+ * should reap an empty list element. We do this if the
+ * empty list element exists at or below the query base
+ * (i.e., it's not part of the walk, but a failed find
+ * on a more specific query e.g., for below the
+ * `route-entry` element for a query
+ * `.../route-entry/metric` where the list element had
+ * no metric value.
+ *
+ * However, if the user query is for a key of a list
+ * element, then when we reach that list element it will
+ * have no non-key children, check for this condition
+ * and do not reap if true.
+ */
+ if (!list_start && ni->inner &&
+ !lyd_child_no_keys(ni->inner) &&
+ /* not the top element with a key match */
+ !((darr_ilen(ys->node_infos) ==
+ darr_ilen(ys->schema_path) - 1) &&
+ lysc_is_key((*darr_last(ys->schema_path)))) &&
+ /* is this at or below the base? */
+ darr_ilen(ys->node_infos) <= ys->query_base_level)
+ ys_free_inner(ys, ni);
+
+
+ if (!list_entry) {
+ /*
+ * List Iteration Done
+ * -------------------
+ */
+
+ /*
+ * Grab next sibling of the list node
+ */
+ if (is_specific_node)
+ sib = NULL;
+ else
+ sib = nb_op_sib_next(ys, sib);
+
+ /*
+ * If we are at the walk root (base) level then
+ * that specifies a list and we are done iterating
+ * the list, so we are done with the walk entirely.
+ */
+ if (!sib && at_clevel == ys->walk_root_level) {
+ ret = NB_OK;
+ goto done;
+ }
+
+ /*
+ * Pop the our list node info back to our
+ * parent.
+ *
+ * We only do this if we've already pushed a
+ * node for the current list schema. For
+ * `list_start` this hasn't happened yet, as
+ * would have happened below. So when list_start
+ * is true but list_entry if NULL we
+ * are processing an empty list.
+ */
+ if (!list_start)
+ ys_pop_inner(ys);
+
+ /*
+ * We should never be below the walk root
+ */
+ assert(darr_lasti(ys->node_infos) >=
+ ys->walk_root_level);
+
+ /* Move on to the sibling of the list node */
+ continue;
+ }
+
+ /*
+ * From here on, we have selected a new top node_info
+ * list entry (either newly pushed or replacing the
+ * previous entry in the walk), and we are filling in
+ * the details.
+ */
+
+ if (list_start) {
+ /*
+ * Starting iteration of a list type or
+ * processing a specific entry, push the list
+ * node_info on stack.
+ */
+ ni = darr_appendz(ys->node_infos);
+ pni = &ni[-1]; /* memory may have moved */
+ ni->has_lookup_next = nn->cbs.lookup_next !=
+ NULL;
+ ni->lookup_next_ok = ((!pni && ys->finish) ||
+ pni->lookup_next_ok) &&
+ ni->has_lookup_next;
+ ni->query_specific_entry = is_specific_node;
+ ni->niters = 0;
+ ni->nents = 0;
+
+ /* this will be our predicate-less xpath */
+ ys->xpath = nb_op_get_child_path(ys->xpath, sib,
+ ys->xpath);
+ } else {
+ /*
+ * Reset our xpath to the list node (i.e.,
+ * remove the entry predicates)
+ */
+ if (ni->query_specific_entry) {
+ flog_warn(EC_LIB_NB_OPERATIONAL_DATA,
+ "%s: unexpected state",
+ __func__);
+ }
+ assert(!ni->query_specific_entry);
+ len = strlen(sib->name) + 1; /* "/sibname" */
+ if (pni)
+ len += pni->xpath_len;
+ darr_setlen(ys->xpath, len + 1);
+ ys->xpath[len] = 0;
+ ni->xpath_len = len;
+ }
+
+ /* Need to get keys. */
+
+ if (!CHECK_FLAG(nn->flags, F_NB_NODE_KEYLESS_LIST)) {
+ ret = nb_callback_get_keys(nn, list_entry,
+ &ni->keys);
+ if (ret) {
+ darr_pop(ys->node_infos);
+ ret = NB_ERR_RESOURCE;
+ goto done;
+ }
+ }
+ /*
+ * Append predicates to xpath.
+ */
+ len = darr_strlen(ys->xpath);
+ if (ni->keys.num) {
+ yang_get_key_preds(ys->xpath + len, sib,
+ &ni->keys,
+ darr_cap(ys->xpath) - len);
+ } else {
+ /* add a position predicate (1s based?) */
+ darr_ensure_avail(ys->xpath, 10);
+ snprintf(ys->xpath + len,
+ darr_cap(ys->xpath) - len + 1, "[%u]",
+ ni->nents + 1);
+ }
+ darr_setlen(ys->xpath,
+ strlen(ys->xpath + len) + len + 1);
+ ni->xpath_len = darr_strlen(ys->xpath);
+
+ /*
+ * Create the new list entry node.
+ */
+
+ if (!node) {
+ err = yang_lyd_new_list((struct lyd_node_inner *)
+ ni[-1]
+ .inner,
+ sib, &ni->keys, &node);
+ if (err) {
+ darr_pop(ys->node_infos);
+ ret = NB_ERR_RESOURCE;
+ goto done;
+ }
+ }
+
+ /*
+ * Save the new list entry with the list node info
+ */
+ ni->inner = node;
+ ni->schema = node->schema;
+ ni->list_entry = list_entry;
+ ni->niters += 1;
+ ni->nents += 1;
+
+ /* Skip over the key children, they've been created. */
+ sib = nb_op_sib_first(ys, sib);
+ continue;
+
+ default:
+ /*FALLTHROUGH*/
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ /* These schema types are not currently handled */
+ flog_warn(EC_LIB_NB_OPERATIONAL_DATA,
+ "%s: unsupported schema node type: %s",
+ __func__, lys_nodetype2str(sib->nodetype));
+ sib = nb_op_sib_next(ys, sib);
+ continue;
+ }
+ }
+
+done:
+ darr_free(xpath_child);
+ return ret;
+}
+
+static void nb_op_walk_continue(struct event *thread)
+{
+ struct nb_op_yield_state *ys = EVENT_ARG(thread);
+ enum nb_error ret = NB_OK;
+
+ DEBUGD(&nb_dbg_cbs_state, "northbound oper-state: resuming %s",
+ ys->xpath);
+
+ nb_op_resume_data_tree(ys);
+
+ /* if we've popped past the walk start level we're done */
+ if (darr_lasti(ys->node_infos) < ys->walk_root_level)
+ goto finish;
+
+ /* otherwise we are at a resumable node */
+ assert(darr_last(ys->node_infos)->has_lookup_next);
+
+ ret = __walk(ys, true);
+ if (ret == NB_YIELD) {
+ if (nb_op_yield(ys) != NB_OK) {
+ if (ys->should_batch)
+ goto stopped;
+ else
+ goto finish;
+ }
+ return;
+ }
+finish:
+ (*ys->finish)(ys_root_node(ys), ys->finish_arg, ret);
+stopped:
+ nb_op_free_yield_state(ys, false);
+}
+
+static void __free_siblings(struct lyd_node *this)
+{
+ struct lyd_node *next, *sib;
+ uint count = 0;
+
+ LY_LIST_FOR_SAFE(lyd_first_sibling(this), next, sib)
+ {
+ if (lysc_is_key(sib->schema))
+ continue;
+ if (sib == this)
+ continue;
+ lyd_free_tree(sib);
+ count++;
+ }
+ DEBUGD(&nb_dbg_events, "NB oper-state: deleted %u siblings", count);
+}
+
+/*
+ * Trim Algorithm:
+ *
+ * Delete final lookup-next list node and subtree, leave stack slot with keys.
+ *
+ * Then walking up the stack, delete all siblings except:
+ * 1. right-most container or list node (must be lookup-next by design)
+ * 2. keys supporting existing parent list node.
+ *
+ * NOTE the topmost node on the stack will be the final lookup-nexxt list node,
+ * as we only yield on lookup-next list nodes.
+ *
+ */
+static void nb_op_trim_yield_state(struct nb_op_yield_state *ys)
+{
+ struct nb_op_node_info *ni;
+ int i = darr_lasti(ys->node_infos);
+
+ assert(i >= 0);
+
+ DEBUGD(&nb_dbg_events, "NB oper-state: start trimming: top: %d", i);
+
+ ni = &ys->node_infos[i];
+ assert(ni->has_lookup_next);
+
+ DEBUGD(&nb_dbg_events, "NB oper-state: deleting tree at level %d", i);
+ __free_siblings(ni->inner);
+ ys_free_inner(ys, ni);
+
+ while (--i > 0) {
+ DEBUGD(&nb_dbg_events,
+ "NB oper-state: deleting siblings at level: %d", i);
+ __free_siblings(ys->node_infos[i].inner);
+ }
+ DEBUGD(&nb_dbg_events, "NB oper-state: stop trimming: new top: %d",
+ (int)darr_lasti(ys->node_infos));
+}
+
+static enum nb_error nb_op_yield(struct nb_op_yield_state *ys)
+{
+ enum nb_error ret;
+ 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);
+
+ if (ys->should_batch) {
+ /*
+ * TODO: add ability of finish to influence the timer.
+ * This will allow, for example, flow control based on how long
+ * it takes finish to process the batch.
+ */
+ ret = (*ys->finish)(ys_root_node(ys), ys->finish_arg, NB_YIELD);
+ if (ret != NB_OK)
+ return ret;
+ /* now trim out that data we just "finished" */
+ nb_op_trim_yield_state(ys);
+
+ }
+
+ event_add_timer_tv(event_loop, nb_op_walk_continue, ys, &tv,
+ &ys->walk_ev);
+ return NB_OK;
+}
+
+static enum nb_error nb_op_ys_init_schema_path(struct nb_op_yield_state *ys,
+ struct nb_node **last)
+{
+ struct nb_node **nb_nodes = NULL;
+ const struct lysc_node *sn;
+ struct nb_node *nblast;
+ char *s, *s2;
+ int count;
+ uint i;
+
+ /*
+ * Get the schema node stack for the entire query string
+ *
+ * The user might pass in something like "//metric" which may resolve to
+ * more than one schema node ("trunks"). nb_node_find() returns a single
+ * node though. We should expand the functionality to get the set of
+ * nodes that matches the xpath (not path) query and save that set in
+ * the yield state. Then we should do a walk using the users query
+ * string over each schema trunk in the set.
+ */
+ nblast = nb_node_find(ys->xpath);
+ if (!nblast) {
+ nb_nodes = nb_nodes_find(ys->xpath);
+ nblast = darr_len(nb_nodes) ? nb_nodes[0] : NULL;
+ darr_free(nb_nodes);
+ }
+ if (!nblast) {
+ flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
+ "%s: unknown data path: %s", __func__, ys->xpath);
+ return NB_ERR;
+ }
+ *last = nblast;
+
+ /*
+ * Create a stack of schema nodes one element per node in the query
+ * path, only the top (last) element may be a non-container type.
+ *
+ * NOTE: appears to be a bug in nb_node linkage where parent can be NULL,
+ * or I'm misunderstanding the code, in any case we use the libyang
+ * linkage to walk which works fine.
+ *
+ * XXX: we don't actually support choice/case yet, they are container
+ * types in the libyang schema, but won't be in data so our length
+ * checking gets messed up.
+ */
+ for (sn = nblast->snode, count = 0; sn; count++, sn = sn->parent)
+ if (sn != nblast->snode)
+ assert(CHECK_FLAG(sn->nodetype,
+ LYS_CONTAINER | LYS_LIST |
+ LYS_CHOICE | LYS_CASE));
+ /* create our arrays */
+ darr_append_n(ys->schema_path, count);
+ darr_append_n(ys->query_tokens, count);
+ darr_append_nz(ys->non_specific_predicate, count);
+ for (sn = nblast->snode; sn; sn = sn->parent)
+ ys->schema_path[--count] = sn;
+
+ /*
+ * Now tokenize the query string and get pointers to each token
+ */
+
+ /* Get copy of query string start after initial '/'s */
+ s = ys->xpath;
+ while (*s && *s == '/')
+ s++;
+ ys->query_tokstr = darr_strdup(s);
+ s = ys->query_tokstr;
+
+ darr_foreach_i (ys->schema_path, i) {
+ const char *modname = ys->schema_path[i]->module->name;
+ const char *name = ys->schema_path[i]->name;
+ int nlen = strlen(name);
+ int mnlen = 0;
+
+ /*
+ * Technically the query_token for choice/case should probably be pointing at
+ * the child (leaf) rather than the parent (container), however,
+ * we only use these for processing list nodes so KISS.
+ */
+ if (CHECK_FLAG(ys->schema_path[i]->nodetype,
+ LYS_CASE | LYS_CHOICE)) {
+ ys->query_tokens[i] = ys->query_tokens[i - 1];
+ continue;
+ }
+
+ while (true) {
+ s2 = strstr(s, name);
+ if (!s2)
+ goto error;
+
+ if (s2[-1] == ':') {
+ mnlen = strlen(modname) + 1;
+ if (ys->query_tokstr > s2 - mnlen ||
+ strncmp(s2 - mnlen, modname, mnlen - 1))
+ goto error;
+ s2 -= mnlen;
+ nlen += mnlen;
+ }
+
+ s = s2;
+ if ((i == 0 || s[-1] == '/') &&
+ (s[nlen] == 0 || s[nlen] == '[' || s[nlen] == '/'))
+ break;
+ /*
+ * Advance past the incorrect match, must have been
+ * part of previous predicate.
+ */
+ s += nlen;
+ }
+
+ /* NUL terminate previous token and save this one */
+ if (i > 0)
+ s[-1] = 0;
+ ys->query_tokens[i] = s;
+ s += nlen;
+ }
+
+ /* NOTE: need to subtract choice/case nodes when these are supported */
+ ys->query_base_level = darr_lasti(ys->schema_path);
+
+ return NB_OK;
+
+error:
+ darr_free(ys->query_tokstr);
+ darr_free(ys->schema_path);
+ darr_free(ys->query_tokens);
+ darr_free(ys->non_specific_predicate);
+ return NB_ERR;
+}
+
+
+/**
+ * nb_op_walk_start() - Start walking oper-state directed by query string.
+ * @ys: partially initialized yield state for this walk.
+ *
+ */
+static enum nb_error nb_op_walk_start(struct nb_op_yield_state *ys)
+{
+ struct nb_node *nblast;
+ enum nb_error ret;
+
+ /*
+ * Get nb_node path (stack) corresponding to the xpath query
+ */
+ ret = nb_op_ys_init_schema_path(ys, &nblast);
+ if (ret != NB_OK)
+ return ret;
+
+
+ /*
+ * Get the node_info path (stack) corresponding to the uniquely
+ * resolvable data nodes from the beginning of the xpath query.
+ */
+ ret = nb_op_ys_init_node_infos(ys);
+ if (ret != NB_OK)
+ return ret;
+
+ return __walk(ys, false);
+}
+
+
+void *nb_oper_walk(const char *xpath, struct yang_translator *translator,
+ uint32_t flags, bool should_batch, nb_oper_data_cb cb,
+ void *cb_arg, nb_oper_data_finish_cb finish, void *finish_arg)
+{
+ struct nb_op_yield_state *ys;
+ enum nb_error ret;
+
+ ys = nb_op_create_yield_state(xpath, translator, flags, should_batch,
+ cb, cb_arg, finish, finish_arg);
+
+ ret = nb_op_walk_start(ys);
+ if (ret == NB_YIELD) {
+ if (nb_op_yield(ys) != NB_OK) {
+ if (ys->should_batch)
+ goto stopped;
+ else
+ goto finish;
+ }
+ return ys;
+ }
+finish:
+ (void)(*ys->finish)(ys_root_node(ys), ys->finish_arg, ret);
+stopped:
+ nb_op_free_yield_state(ys, false);
+ return NULL;
+}
+
+
+void nb_oper_cancel_walk(void *walk)
+{
+ if (walk)
+ nb_op_free_yield_state(walk, false);
+}
+
+
+void nb_oper_cancel_all_walks(void)
+{
+ struct nb_op_yield_state *ys;
+
+ frr_each_safe (nb_op_walks, &nb_op_walks, ys)
+ nb_oper_cancel_walk(ys);
+}
+
+
+/*
+ * The old API -- remove when we've update the users to yielding.
+ */
+enum nb_error nb_oper_iterate_legacy(const char *xpath,
+ struct yang_translator *translator,
+ uint32_t flags, nb_oper_data_cb cb,
+ void *cb_arg, struct lyd_node **tree)
+{
+ struct nb_op_yield_state *ys;
+ enum nb_error ret;
+
+ ys = nb_op_create_yield_state(xpath, translator, flags, false, cb,
+ cb_arg, NULL, NULL);
+
+ ret = nb_op_walk_start(ys);
+ assert(ret != NB_YIELD);
+
+ if (tree && ret == NB_OK)
+ *tree = ys_root_node(ys);
+ else {
+ if (ys_root_node(ys))
+ yang_dnode_free(ys_root_node(ys));
+ if (tree)
+ *tree = NULL;
+ }
+
+ nb_op_free_yield_state(ys, true);
+ return ret;
+}
+
+void nb_oper_init(struct event_loop *loop)
+{
+ event_loop = loop;
+ nb_op_walks_init(&nb_op_walks);
+}
+
+void nb_oper_terminate(void)
+{
+ nb_oper_cancel_all_walks();
+}
diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c
index 7fd4af8356..640334926d 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;
@@ -118,6 +116,9 @@ static int yang_data_frr2sr(struct yang_data *frr_data, sr_val_t *sr_data)
sr_data->type = SR_INT64_T;
sr_data->data.int64_val = yang_str2int64(frr_data->value);
break;
+ case LY_TYPE_LEAFREF:
+ sr_val_set_str_data(sr_data, SR_STRING_T, frr_data->value);
+ break;
case LY_TYPE_STRING:
sr_val_set_str_data(sr_data, SR_STRING_T, frr_data->value);
break;
@@ -137,6 +138,11 @@ static int yang_data_frr2sr(struct yang_data *frr_data, sr_val_t *sr_data)
sr_data->type = SR_UINT64_T;
sr_data->data.uint64_val = yang_str2uint64(frr_data->value);
break;
+ case LY_TYPE_UNION:
+ /* No way to deal with this using un-typed yang_data object */
+ sr_val_set_str_data(sr_data, SR_STRING_T, frr_data->value);
+ break;
+ case LY_TYPE_UNKNOWN:
default:
return -1;
}
@@ -178,12 +184,12 @@ static int frr_sr_process_change(struct nb_config *candidate,
/* Map operation values. */
switch (sr_op) {
case SR_OP_CREATED:
+ nb_op = NB_OP_CREATE;
+ break;
case SR_OP_MODIFIED:
- if (nb_operation_is_valid(NB_OP_CREATE, nb_node->snode))
- nb_op = NB_OP_CREATE;
- else if (nb_operation_is_valid(NB_OP_MODIFY, nb_node->snode)) {
+ if (nb_is_operation_allowed(nb_node, NB_OP_MODIFY))
nb_op = NB_OP_MODIFY;
- } else
+ else
/* Ignore list keys modifications. */
return NB_OK;
break;
@@ -193,7 +199,7 @@ static int frr_sr_process_change(struct nb_config *candidate,
* notified about the removal of all of its leafs, even the ones
* that are non-optional. We need to ignore these notifications.
*/
- if (!nb_operation_is_valid(NB_OP_DESTROY, nb_node->snode))
+ if (!nb_is_operation_allowed(nb_node, NB_OP_DESTROY))
return NB_OK;
nb_op = NB_OP_DESTROY;
@@ -213,7 +219,7 @@ static int frr_sr_process_change(struct nb_config *candidate,
ret = nb_candidate_edit(candidate, nb_node, nb_op, xpath, NULL, data);
yang_data_free(data);
- if (ret != NB_OK && ret != NB_ERR_NOT_FOUND) {
+ if (ret != NB_OK) {
flog_warn(
EC_LIB_NB_CANDIDATE_EDIT_ERROR,
"%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
@@ -271,11 +277,12 @@ static int frr_sr_config_change_cb_prepare(sr_session_ctx_t *session,
ret = nb_candidate_commit_prepare(context, candidate, NULL,
&transaction, false, false, errmsg,
sizeof(errmsg));
- if (ret != NB_OK && ret != NB_ERR_NO_CHANGES)
- flog_warn(
- EC_LIB_LIBSYSREPO,
- "%s: failed to prepare configuration transaction: %s (%s)",
- __func__, nb_err_name(ret), errmsg);
+ if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) {
+ flog_warn(EC_LIB_LIBSYSREPO,
+ "%s: failed to prepare configuration transaction: %s (%s)",
+ __func__, nb_err_name(ret), errmsg);
+ sr_session_set_error_message(session, errmsg);
+ }
if (!transaction)
nb_config_free(candidate);
@@ -340,6 +347,8 @@ static int frr_sr_config_change_cb(sr_session_ctx_t *session, uint32_t sub_id,
return frr_sr_config_change_cb_apply(session, module_name);
case SR_EV_ABORT:
return frr_sr_config_change_cb_abort(session, module_name);
+ case SR_EV_RPC:
+ case SR_EV_UPDATE:
default:
flog_err(EC_LIB_LIBSYSREPO, "%s: unexpected sysrepo event: %u",
__func__, sr_ev);
@@ -347,39 +356,16 @@ static int frr_sr_config_change_cb(sr_session_ctx_t *session, uint32_t sub_id,
}
}
-static int frr_sr_state_data_iter_cb(const struct lysc_node *snode,
- struct yang_translator *translator,
- struct yang_data *data, void *arg)
-{
- struct lyd_node *dnode = arg;
- LY_ERR ly_errno;
-
- ly_errno = 0;
- ly_errno = lyd_new_path(NULL, ly_native_ctx, data->xpath, data->value,
- 0, &dnode);
- if (ly_errno) {
- flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed",
- __func__);
- yang_data_free(data);
- return NB_ERR;
- }
-
- yang_data_free(data);
- return NB_OK;
-}
-
/* Callback for state retrieval. */
static int frr_sr_state_cb(sr_session_ctx_t *session, uint32_t sub_id,
const char *module_name, const char *xpath,
const char *request_xpath, uint32_t request_id,
struct lyd_node **parent, void *private_ctx)
{
- struct lyd_node *dnode;
+ struct lyd_node *dnode = NULL;
dnode = *parent;
- if (nb_oper_data_iterate(request_xpath, NULL, 0,
- frr_sr_state_data_iter_cb, dnode)
- != NB_OK) {
+ if (nb_oper_iterate_legacy(request_xpath, NULL, 0, NULL, NULL, &dnode)) {
flog_warn(EC_LIB_NB_OPERATIONAL_DATA,
"%s: failed to obtain operational data [xpath %s]",
__func__, xpath);
diff --git a/lib/ntop.c b/lib/ntop.c
index 49e1b152c9..89b4d5ecdc 100644
--- a/lib/ntop.c
+++ b/lib/ntop.c
@@ -158,7 +158,7 @@ inet4:
return dst;
}
-#if !defined(INET_NTOP_NO_OVERRIDE) && !defined(__APPLE__)
+#if !defined(INET_NTOP_NO_OVERRIDE)
/* we want to override libc inet_ntop, but make sure it shows up in backtraces
* as frr_inet_ntop (to avoid confusion while debugging)
*/
diff --git a/lib/pbr.h b/lib/pbr.h
index e8563afb3b..fe2d32a44a 100644
--- a/lib/pbr.h
+++ b/lib/pbr.h
@@ -1,6 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* Policy Based Routing (PBR) main header
* Copyright (C) 2018 6WIND
+ * Portions:
+ * Copyright (c) 2021 The MITRE Corporation.
+ * Copyright (c) 2023 LabN Consulting, L.L.C.
*/
#ifndef _PBR_H
@@ -25,30 +28,44 @@ extern "C" {
* specified.
*/
struct pbr_filter {
- uint32_t filter_bm; /* not encoded by zapi
- */
-#define PBR_FILTER_SRC_IP (1 << 0)
-#define PBR_FILTER_DST_IP (1 << 1)
-#define PBR_FILTER_SRC_PORT (1 << 2)
-#define PBR_FILTER_DST_PORT (1 << 3)
-#define PBR_FILTER_FWMARK (1 << 4)
-#define PBR_FILTER_PROTO (1 << 5)
-#define PBR_FILTER_SRC_PORT_RANGE (1 << 6)
-#define PBR_FILTER_DST_PORT_RANGE (1 << 7)
-#define PBR_FILTER_DSFIELD (1 << 8)
-#define PBR_FILTER_IP_PROTOCOL (1 << 9)
+ uint32_t filter_bm;
+#define PBR_FILTER_SRC_IP (1 << 0)
+#define PBR_FILTER_DST_IP (1 << 1)
+#define PBR_FILTER_SRC_PORT (1 << 2)
+#define PBR_FILTER_DST_PORT (1 << 3)
+#define PBR_FILTER_FWMARK (1 << 4)
+#define PBR_FILTER_IP_PROTOCOL (1 << 5)
+#define PBR_FILTER_SRC_PORT_RANGE (1 << 6)
+#define PBR_FILTER_DST_PORT_RANGE (1 << 7)
+#define PBR_FILTER_DSCP (1 << 8)
+#define PBR_FILTER_ECN (1 << 9)
+#define PBR_FILTER_PCP (1 << 10)
+#define PBR_FILTER_VLAN_FLAGS (1 << 11)
+#define PBR_FILTER_VLAN_ID (1 << 12)
#define PBR_DSFIELD_DSCP (0xfc) /* Upper 6 bits of DS field: DSCP */
#define PBR_DSFIELD_ECN (0x03) /* Lower 2 bits of DS field: BCN */
- /* Source and Destination IP address with masks. */
+#define PBR_PCP (0x07) /* 3-bit value 0..7 for prioritization*/
+
+#define PBR_VLAN_FLAGS_NO_WILD 0
+#define PBR_VLAN_FLAGS_TAGGED (1 << 0)
+#define PBR_VLAN_FLAGS_UNTAGGED (1 << 1)
+#define PBR_VLAN_FLAGS_UNTAGGED_0 (1 << 2)
+
+ /* Source and Destination IP address with masks */
struct prefix src_ip;
struct prefix dst_ip;
- /* Source and Destination higher-layer (TCP/UDP) port numbers. */
+ /* Source and Destination layer 4 (TCP/UDP/etc.) port numbers */
uint16_t src_port;
uint16_t dst_port;
+ /* Filter by VLAN and prioritization */
+ uint8_t pcp;
+ uint16_t vlan_id;
+ uint16_t vlan_flags;
+
/* Filter by Differentiated Services field */
uint8_t dsfield; /* DSCP (6 bits) & ECN (2 bits) */
@@ -69,14 +86,39 @@ struct pbr_filter {
* the user criteria may directly point to a table too.
*/
struct pbr_action {
+ uint32_t flags;
+
+#define PBR_ACTION_TABLE (1 << 0)
+#define PBR_ACTION_QUEUE_ID (1 << 1)
+#define PBR_ACTION_PCP (1 << 2)
+#define PBR_ACTION_VLAN_ID (1 << 3)
+#define PBR_ACTION_VLAN_STRIP_INNER_ANY (1 << 4)
+#define PBR_ACTION_SRC_IP (1 << 5)
+#define PBR_ACTION_DST_IP (1 << 6)
+#define PBR_ACTION_SRC_PORT (1 << 7)
+#define PBR_ACTION_DST_PORT (1 << 8)
+#define PBR_ACTION_DSCP (1 << 9)
+#define PBR_ACTION_ECN (1 << 10)
+#define PBR_ACTION_DROP (1 << 11) /* nexthop == blackhole */
+
+ uint32_t table;
+ uint32_t queue_id;
+
/* VLAN */
uint8_t pcp;
uint16_t vlan_id;
- uint16_t vlan_flags;
- uint32_t queue_id;
+ /* Source and Destination IP addresses */
+ union sockunion src_ip;
+ union sockunion dst_ip;
- uint32_t table;
+ /* Source and Destination layer 4 (TCP/UDP/etc.) port numbers */
+ uint32_t src_port;
+ uint32_t dst_port;
+
+ /* Differentiated Services field */
+ uint8_t dscp; /* stored here already shifted to upper 6 bits */
+ uint8_t ecn; /* stored here as lower 2 bits */
};
/*
@@ -88,6 +130,7 @@ struct pbr_action {
*/
struct pbr_rule {
vrf_id_t vrf_id;
+ uint8_t family; /* netlink: select which rule database */
uint32_t seq;
uint32_t priority;
@@ -95,7 +138,7 @@ struct pbr_rule {
struct pbr_filter filter;
struct pbr_action action;
- char ifname[INTERFACE_NAMSIZ + 1];
+ char ifname[IFNAMSIZ + 1];
};
/* TCP flags value shared
@@ -130,8 +173,8 @@ struct pbr_rule {
#define MATCH_FLOW_LABEL_SET (1 << 12)
#define MATCH_FLOW_LABEL_INVERSE_SET (1 << 13)
-extern int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s,
- struct pbr_rule *zrule);
+extern int zapi_pbr_rule_encode(struct stream *s, struct pbr_rule *r);
+extern bool zapi_pbr_rule_decode(struct stream *s, struct pbr_rule *r);
#ifdef __cplusplus
}
diff --git a/lib/pid_output.c b/lib/pid_output.c
index 064a7bb47f..ce1b7d1969 100644
--- a/lib/pid_output.c
+++ b/lib/pid_output.c
@@ -5,6 +5,7 @@
*/
#include <zebra.h>
+#include <sys/stat.h>
#include <fcntl.h>
#include <log.h>
#include "lib/version.h"
diff --git a/lib/plist.c b/lib/plist.c
index e286a32f92..618d92b549 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -402,14 +402,55 @@ static void prefix_list_trie_del(struct prefix_list *plist,
}
}
+/**
+ * Find duplicated prefix entry (same prefix but different entry) in prefix
+ * list.
+ */
+static bool prefix_list_entry_is_duplicated(struct prefix_list *list,
+ struct prefix_list_entry *entry)
+{
+ size_t depth, maxdepth = list->master->trie_depth;
+ uint8_t byte, *bytes = entry->prefix.u.val;
+ size_t validbits = entry->prefix.prefixlen;
+ struct pltrie_table *table = list->trie;
+ struct prefix_list_entry *pentry;
+
+ for (depth = 0; validbits > PLC_BITS && depth < maxdepth - 1; depth++) {
+ byte = bytes[depth];
+ if (!table->entries[byte].next_table)
+ return NULL;
+
+ table = table->entries[byte].next_table;
+ validbits -= PLC_BITS;
+ }
+
+ byte = bytes[depth];
+ if (validbits > PLC_BITS)
+ pentry = table->entries[byte].final_chain;
+ else
+ pentry = table->entries[byte].up_chain;
+
+ for (; pentry; pentry = pentry->next_best) {
+ if (pentry == entry)
+ continue;
+ if (prefix_same(&pentry->prefix, &entry->prefix))
+ return true;
+ }
+
+ return false;
+}
void prefix_list_entry_delete(struct prefix_list *plist,
struct prefix_list_entry *pentry,
int update_list)
{
+ bool duplicate;
+
if (plist == NULL || pentry == NULL)
return;
+ duplicate = prefix_list_entry_is_duplicated(plist, pentry);
+
prefix_list_trie_del(plist, pentry);
if (pentry->prev)
@@ -421,8 +462,10 @@ void prefix_list_entry_delete(struct prefix_list *plist,
else
plist->tail = pentry->prev;
- route_map_notify_pentry_dependencies(plist->name, pentry,
- RMAP_EVENT_PLIST_DELETED);
+ if (!duplicate)
+ route_map_notify_pentry_dependencies(plist->name, pentry,
+ RMAP_EVENT_PLIST_DELETED);
+
prefix_list_entry_free(pentry);
plist->count--;
@@ -557,11 +600,14 @@ static void prefix_list_entry_add(struct prefix_list *plist,
void prefix_list_entry_update_start(struct prefix_list_entry *ple)
{
struct prefix_list *pl = ple->pl;
+ bool duplicate;
/* Not installed, nothing to do. */
if (!ple->installed)
return;
+ duplicate = prefix_list_entry_is_duplicated(pl, ple);
+
prefix_list_trie_del(pl, ple);
/* List manipulation: shameless copy from `prefix_list_entry_delete`. */
@@ -574,8 +620,9 @@ void prefix_list_entry_update_start(struct prefix_list_entry *ple)
else
pl->tail = ple->prev;
- route_map_notify_pentry_dependencies(pl->name, ple,
- RMAP_EVENT_PLIST_DELETED);
+ if (!duplicate)
+ route_map_notify_pentry_dependencies(pl->name, ple,
+ RMAP_EVENT_PLIST_DELETED);
pl->count--;
route_map_notify_dependencies(pl->name, RMAP_EVENT_PLIST_DELETED);
@@ -1182,7 +1229,7 @@ static int vty_clear_prefix_list(struct vty *vty, afi_t afi, const char *name,
DEFPY (show_ip_prefix_list,
show_ip_prefix_list_cmd,
- "show ip prefix-list [WORD [seq$dseq (1-4294967295)$arg]] [json$uj]",
+ "show ip prefix-list [PREFIXLIST4_NAME$name [seq$dseq (1-4294967295)$arg]] [json$uj]",
SHOW_STR
IP_STR
PREFIX_LIST_STR
@@ -1195,13 +1242,13 @@ DEFPY (show_ip_prefix_list,
if (dseq)
dtype = sequential_display;
- return vty_show_prefix_list(vty, AFI_IP, prefix_list, arg_str, dtype,
+ return vty_show_prefix_list(vty, AFI_IP, name, arg_str, dtype,
!!uj);
}
DEFPY (show_ip_prefix_list_prefix,
show_ip_prefix_list_prefix_cmd,
- "show ip prefix-list WORD A.B.C.D/M$prefix [longer$dl|first-match$dfm]",
+ "show ip prefix-list PREFIXLIST4_NAME$name A.B.C.D/M$prefix [longer$dl|first-match$dfm]",
SHOW_STR
IP_STR
PREFIX_LIST_STR
@@ -1216,13 +1263,13 @@ DEFPY (show_ip_prefix_list_prefix,
else if (dfm)
dtype = first_match_display;
- return vty_show_prefix_list_prefix(vty, AFI_IP, prefix_list, prefix_str,
+ return vty_show_prefix_list_prefix(vty, AFI_IP, name, prefix_str,
dtype);
}
DEFPY (show_ip_prefix_list_summary,
show_ip_prefix_list_summary_cmd,
- "show ip prefix-list summary [WORD$prefix_list] [json$uj]",
+ "show ip prefix-list summary [PREFIXLIST4_NAME$name] [json$uj]",
SHOW_STR
IP_STR
PREFIX_LIST_STR
@@ -1230,13 +1277,13 @@ DEFPY (show_ip_prefix_list_summary,
"Name of a prefix list\n"
JSON_STR)
{
- return vty_show_prefix_list(vty, AFI_IP, prefix_list, NULL,
+ return vty_show_prefix_list(vty, AFI_IP, name, NULL,
summary_display, !!uj);
}
DEFPY (show_ip_prefix_list_detail,
show_ip_prefix_list_detail_cmd,
- "show ip prefix-list detail [WORD$prefix_list] [json$uj]",
+ "show ip prefix-list detail [PREFIXLIST4_NAME$name] [json$uj]",
SHOW_STR
IP_STR
PREFIX_LIST_STR
@@ -1244,25 +1291,25 @@ DEFPY (show_ip_prefix_list_detail,
"Name of a prefix list\n"
JSON_STR)
{
- return vty_show_prefix_list(vty, AFI_IP, prefix_list, NULL,
+ return vty_show_prefix_list(vty, AFI_IP, name, NULL,
detail_display, !!uj);
}
DEFPY (clear_ip_prefix_list,
clear_ip_prefix_list_cmd,
- "clear ip prefix-list [WORD [A.B.C.D/M$prefix]]",
+ "clear ip prefix-list [PREFIXLIST4_NAME$name [A.B.C.D/M$prefix]]",
CLEAR_STR
IP_STR
PREFIX_LIST_STR
"Name of a prefix list\n"
"IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
{
- return vty_clear_prefix_list(vty, AFI_IP, prefix_list, prefix_str);
+ return vty_clear_prefix_list(vty, AFI_IP, name, prefix_str);
}
DEFPY (show_ipv6_prefix_list,
show_ipv6_prefix_list_cmd,
- "show ipv6 prefix-list [WORD [seq$dseq (1-4294967295)$arg]] [json$uj]",
+ "show ipv6 prefix-list [PREFIXLIST6_NAME$name [seq$dseq (1-4294967295)$arg]] [json$uj]",
SHOW_STR
IPV6_STR
PREFIX_LIST_STR
@@ -1275,13 +1322,13 @@ DEFPY (show_ipv6_prefix_list,
if (dseq)
dtype = sequential_display;
- return vty_show_prefix_list(vty, AFI_IP6, prefix_list, arg_str, dtype,
+ return vty_show_prefix_list(vty, AFI_IP6, name, arg_str, dtype,
!!uj);
}
DEFPY (show_ipv6_prefix_list_prefix,
show_ipv6_prefix_list_prefix_cmd,
- "show ipv6 prefix-list WORD X:X::X:X/M$prefix [longer$dl|first-match$dfm]",
+ "show ipv6 prefix-list PREFIXLIST6_NAME$name X:X::X:X/M$prefix [longer$dl|first-match$dfm]",
SHOW_STR
IPV6_STR
PREFIX_LIST_STR
@@ -1296,13 +1343,13 @@ DEFPY (show_ipv6_prefix_list_prefix,
else if (dfm)
dtype = first_match_display;
- return vty_show_prefix_list_prefix(vty, AFI_IP6, prefix_list,
+ return vty_show_prefix_list_prefix(vty, AFI_IP6, name,
prefix_str, dtype);
}
DEFPY (show_ipv6_prefix_list_summary,
show_ipv6_prefix_list_summary_cmd,
- "show ipv6 prefix-list summary [WORD$prefix-list] [json$uj]",
+ "show ipv6 prefix-list summary [PREFIXLIST6_NAME$name] [json$uj]",
SHOW_STR
IPV6_STR
PREFIX_LIST_STR
@@ -1310,13 +1357,13 @@ DEFPY (show_ipv6_prefix_list_summary,
"Name of a prefix list\n"
JSON_STR)
{
- return vty_show_prefix_list(vty, AFI_IP6, prefix_list, NULL,
+ return vty_show_prefix_list(vty, AFI_IP6, name, NULL,
summary_display, !!uj);
}
DEFPY (show_ipv6_prefix_list_detail,
show_ipv6_prefix_list_detail_cmd,
- "show ipv6 prefix-list detail [WORD$prefix-list] [json$uj]",
+ "show ipv6 prefix-list detail [PREFIXLIST6_NAME$name] [json$uj]",
SHOW_STR
IPV6_STR
PREFIX_LIST_STR
@@ -1324,20 +1371,20 @@ DEFPY (show_ipv6_prefix_list_detail,
"Name of a prefix list\n"
JSON_STR)
{
- return vty_show_prefix_list(vty, AFI_IP6, prefix_list, NULL,
+ return vty_show_prefix_list(vty, AFI_IP6, name, NULL,
detail_display, !!uj);
}
DEFPY (clear_ipv6_prefix_list,
clear_ipv6_prefix_list_cmd,
- "clear ipv6 prefix-list [WORD [X:X::X:X/M$prefix]]",
+ "clear ipv6 prefix-list [PREFIXLIST6_NAME$name [X:X::X:X/M$prefix]]",
CLEAR_STR
IPV6_STR
PREFIX_LIST_STR
"Name of a prefix list\n"
"IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n")
{
- return vty_clear_prefix_list(vty, AFI_IP6, prefix_list, prefix_str);
+ return vty_clear_prefix_list(vty, AFI_IP6, name, prefix_str);
}
DEFPY (debug_prefix_list_match,
@@ -1585,12 +1632,26 @@ static void plist_autocomplete(vector comps, struct cmd_token *token)
plist_autocomplete_afi(AFI_IP6, comps, token);
}
+static void plist4_autocomplete(vector comps, struct cmd_token *token)
+{
+ plist_autocomplete_afi(AFI_IP, comps, token);
+}
+
+static void plist6_autocomplete(vector comps, struct cmd_token *token)
+{
+ plist_autocomplete_afi(AFI_IP6, comps, token);
+}
+
static const struct cmd_variable_handler plist_var_handlers[] = {
{/* "prefix-list WORD" */
.varname = "prefix_list",
.completions = plist_autocomplete},
{.tokenname = "PREFIXLIST_NAME",
.completions = plist_autocomplete},
+ {.tokenname = "PREFIXLIST4_NAME",
+ .completions = plist4_autocomplete},
+ {.tokenname = "PREFIXLIST6_NAME",
+ .completions = plist6_autocomplete},
{.completions = NULL}};
diff --git a/lib/prefix.c b/lib/prefix.c
index b8cad910f4..f342c4c1db 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -261,7 +261,7 @@ int evpn_type5_prefix_match(const struct prefix *n, const struct prefix *p)
return 0;
prefixlen = evp->prefix.prefix_addr.ip_prefix_length;
- np = &evp->prefix.prefix_addr.ip.ip.addr;
+ np = evp->prefix.prefix_addr.ip.ip.addrbytes;
/* If n's prefix is longer than p's one return 0. */
if (prefixlen > p->prefixlen)
@@ -355,6 +355,41 @@ void prefix_copy(union prefixptr udest, union prefixconstptr usrc)
}
}
+bool evpn_addr_same(const struct evpn_addr *e1, const struct evpn_addr *e2)
+{
+ if (e1->route_type != e2->route_type)
+ return false;
+ if (e1->route_type == BGP_EVPN_AD_ROUTE)
+ return (!memcmp(&e1->ead_addr.esi.val,
+ &e2->ead_addr.esi.val, ESI_BYTES) &&
+ e1->ead_addr.eth_tag == e2->ead_addr.eth_tag &&
+ !ipaddr_cmp(&e1->ead_addr.ip, &e2->ead_addr.ip));
+ if (e1->route_type == BGP_EVPN_MAC_IP_ROUTE)
+ return (e1->macip_addr.eth_tag == e2->macip_addr.eth_tag &&
+ e1->macip_addr.ip_prefix_length
+ == e2->macip_addr.ip_prefix_length &&
+ !memcmp(&e1->macip_addr.mac,
+ &e2->macip_addr.mac, ETH_ALEN) &&
+ !ipaddr_cmp(&e1->macip_addr.ip, &e2->macip_addr.ip));
+ if (e1->route_type == BGP_EVPN_IMET_ROUTE)
+ return (e1->imet_addr.eth_tag == e2->imet_addr.eth_tag &&
+ e1->imet_addr.ip_prefix_length
+ == e2->imet_addr.ip_prefix_length &&
+ !ipaddr_cmp(&e1->imet_addr.ip, &e2->imet_addr.ip));
+ if (e1->route_type == BGP_EVPN_ES_ROUTE)
+ return (!memcmp(&e1->es_addr.esi.val,
+ &e2->es_addr.esi.val, ESI_BYTES) &&
+ e1->es_addr.ip_prefix_length
+ == e2->es_addr.ip_prefix_length &&
+ !ipaddr_cmp(&e1->es_addr.ip, &e2->es_addr.ip));
+ if (e1->route_type == BGP_EVPN_IP_PREFIX_ROUTE)
+ return (e1->prefix_addr.eth_tag == e2->prefix_addr.eth_tag &&
+ e1->prefix_addr.ip_prefix_length
+ == e2->prefix_addr.ip_prefix_length &&
+ !ipaddr_cmp(&e1->prefix_addr.ip, &e2->prefix_addr.ip));
+ return true;
+}
+
/*
* Return 1 if the address/netmask contained in the prefix structure
* is the same, and else return 0. For this routine, 'same' requires
@@ -387,8 +422,7 @@ int prefix_same(union prefixconstptr up1, union prefixconstptr up2)
sizeof(struct ethaddr)))
return 1;
if (p1->family == AF_EVPN)
- if (!memcmp(&p1->u.prefix_evpn, &p2->u.prefix_evpn,
- sizeof(struct evpn_addr)))
+ if (evpn_addr_same(&p1->u.prefix_evpn, &p2->u.prefix_evpn))
return 1;
if (p1->family == AF_FLOWSPEC) {
if (p1->u.prefix_flowspec.family !=
diff --git a/lib/prefix.h b/lib/prefix.h
index 90b792b33c..14f2695933 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -286,23 +286,25 @@ struct prefix_sg {
struct in_addr grp;
};
+/* clang-format off */
union prefixptr {
- prefixtype(prefixptr, struct prefix, p)
- prefixtype(prefixptr, struct prefix_ipv4, p4)
- prefixtype(prefixptr, struct prefix_ipv6, p6)
- prefixtype(prefixptr, struct prefix_evpn, evp)
- prefixtype(prefixptr, struct prefix_fs, fs)
- prefixtype(prefixptr, struct prefix_rd, rd)
+ uniontype(prefixptr, struct prefix, p)
+ uniontype(prefixptr, struct prefix_ipv4, p4)
+ uniontype(prefixptr, struct prefix_ipv6, p6)
+ uniontype(prefixptr, struct prefix_evpn, evp)
+ uniontype(prefixptr, struct prefix_fs, fs)
+ uniontype(prefixptr, struct prefix_rd, rd)
} TRANSPARENT_UNION;
union prefixconstptr {
- prefixtype(prefixconstptr, const struct prefix, p)
- prefixtype(prefixconstptr, const struct prefix_ipv4, p4)
- prefixtype(prefixconstptr, const struct prefix_ipv6, p6)
- prefixtype(prefixconstptr, const struct prefix_evpn, evp)
- prefixtype(prefixconstptr, const struct prefix_fs, fs)
- prefixtype(prefixconstptr, const struct prefix_rd, rd)
+ uniontype(prefixconstptr, const struct prefix, p)
+ uniontype(prefixconstptr, const struct prefix_ipv4, p4)
+ uniontype(prefixconstptr, const struct prefix_ipv6, p6)
+ uniontype(prefixconstptr, const struct prefix_evpn, evp)
+ uniontype(prefixconstptr, const struct prefix_fs, fs)
+ uniontype(prefixconstptr, const struct prefix_rd, rd)
} TRANSPARENT_UNION;
+/* clang-format on */
#ifndef INET_ADDRSTRLEN
#define INET_ADDRSTRLEN 16
@@ -377,9 +379,9 @@ static inline void ipv4_addr_copy(struct in_addr *dst,
#endif /*s6_addr32*/
/* Prototypes. */
-extern int str2family(const char *);
-extern int afi2family(afi_t);
-extern afi_t family2afi(int);
+extern int str2family(const char *string);
+extern int afi2family(afi_t afi);
+extern afi_t family2afi(int family);
extern const char *family2str(int family);
extern const char *safi2str(safi_t safi);
extern const char *afi2str(afi_t afi);
@@ -409,14 +411,15 @@ extern void prefix_free(struct prefix **p);
extern void prefix_free_lists(void *arg);
extern const char *prefix_family_str(union prefixconstptr pu);
extern int prefix_blen(union prefixconstptr pu);
-extern int str2prefix(const char *, struct prefix *);
+extern int str2prefix(const char *string, struct prefix *prefix);
#define PREFIX2STR_BUFFER PREFIX_STRLEN
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);
-extern const char *prefix2str(union prefixconstptr, char *, int);
+extern const char *prefix2str(union prefixconstptr upfx, char *buffer,
+ int size);
extern int evpn_type5_prefix_match(const struct prefix *evpn_pfx,
const struct prefix *match_pfx);
extern int prefix_match(union prefixconstptr unet, union prefixconstptr upfx);
@@ -427,6 +430,7 @@ extern int prefix_cmp(union prefixconstptr ua, union prefixconstptr ub);
extern int prefix_common_bits(union prefixconstptr ua, union prefixconstptr ub);
extern void prefix_copy(union prefixptr udst, union prefixconstptr usrc);
extern void apply_mask(union prefixptr pu);
+extern bool evpn_addr_same(const struct evpn_addr *e1, const struct evpn_addr *e2);
#ifdef __clang_analyzer__
/* clang-SA doesn't understand transparent unions, making it think that the
@@ -436,36 +440,37 @@ extern void apply_mask(union prefixptr pu);
#define prefix_copy(a, b) ({ memset(a, 0, sizeof(*a)); prefix_copy(a, b); })
#endif
-extern struct prefix *sockunion2hostprefix(const union sockunion *,
+extern struct prefix *sockunion2hostprefix(const union sockunion *su,
struct prefix *p);
-extern void prefix2sockunion(const struct prefix *, union sockunion *);
+extern void prefix2sockunion(const struct prefix *p, union sockunion *su);
-extern int str2prefix_eth(const char *, struct prefix_eth *);
+extern int str2prefix_eth(const char *string, struct prefix_eth *p);
extern struct prefix_ipv4 *prefix_ipv4_new(void);
extern void prefix_ipv4_free(struct prefix_ipv4 **p);
-extern int str2prefix_ipv4(const char *, struct prefix_ipv4 *);
-extern void apply_mask_ipv4(struct prefix_ipv4 *);
+extern int str2prefix_ipv4(const char *string, struct prefix_ipv4 *p);
+extern void apply_mask_ipv4(struct prefix_ipv4 *p);
-extern int prefix_ipv4_any(const struct prefix_ipv4 *);
-extern void apply_classful_mask_ipv4(struct prefix_ipv4 *);
+extern int prefix_ipv4_any(const struct prefix_ipv4 *p);
+extern void apply_classful_mask_ipv4(struct prefix_ipv4 *p);
-extern uint8_t ip_masklen(struct in_addr);
-extern void masklen2ip(const int, struct in_addr *);
+extern uint8_t ip_masklen(struct in_addr addr);
+extern void masklen2ip(const int length, struct in_addr *addr);
/* given the address of a host on a network and the network mask length,
* calculate the broadcast address for that network;
* special treatment for /31 according to RFC3021 section 3.3 */
extern in_addr_t ipv4_broadcast_addr(in_addr_t hostaddr, int masklen);
-extern int netmask_str2prefix_str(const char *, const char *, char *, size_t);
+extern int netmask_str2prefix_str(const char *net_str, const char *mask_str,
+ char *prefix_str, size_t prefix_str_len);
extern struct prefix_ipv6 *prefix_ipv6_new(void);
extern void prefix_ipv6_free(struct prefix_ipv6 **p);
-extern int str2prefix_ipv6(const char *, struct prefix_ipv6 *);
-extern void apply_mask_ipv6(struct prefix_ipv6 *);
+extern int str2prefix_ipv6(const char *str, struct prefix_ipv6 *p);
+extern void apply_mask_ipv6(struct prefix_ipv6 *p);
-extern int ip6_masklen(struct in6_addr);
-extern void masklen2ip6(const int, struct in6_addr *);
+extern int ip6_masklen(struct in6_addr netmask);
+extern void masklen2ip6(const int masklen, struct in6_addr *netmask);
extern int is_zero_mac(const struct ethaddr *mac);
extern bool is_mcast_mac(const struct ethaddr *mac);
diff --git a/lib/printf/README b/lib/printf/README
index ac283642d7..3c3ef74ca8 100644
--- a/lib/printf/README
+++ b/lib/printf/README
@@ -1,6 +1,7 @@
-This is the printf implementation from FreeBSD. It was imported on 2019-05-12,
-from SVN revision 347514 (but the code hadn't been touched for 2 years before
-that.)
+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/glue.c b/lib/printf/glue.c
index 25e2ff37a3..f799378af3 100644
--- a/lib/printf/glue.c
+++ b/lib/printf/glue.c
@@ -273,6 +273,7 @@ static ssize_t printfrr_va(struct fbuf *buf, struct printfrr_eargs *ea,
{
const struct va_format *vaf = ptr;
va_list ap;
+ ssize_t s;
if (!vaf || !vaf->fmt || !vaf->va)
return bputs(buf, "NULL");
@@ -285,6 +286,9 @@ static ssize_t printfrr_va(struct fbuf *buf, struct printfrr_eargs *ea,
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
/* can't format check this */
- return vbprintfrr(buf, vaf->fmt, ap);
+ s = vbprintfrr(buf, vaf->fmt, ap);
#pragma GCC diagnostic pop
+ va_end(ap);
+
+ return s;
}
diff --git a/lib/printf/printf-pos.c b/lib/printf/printf-pos.c
index ac775bea4e..b2ba1a714d 100644
--- a/lib/printf/printf-pos.c
+++ b/lib/printf/printf-pos.c
@@ -355,7 +355,7 @@ reswitch: switch (ch) {
goto rflag;
case 'C':
flags |= LONGINT;
- /*FALLTHROUGH*/
+ fallthrough;
case 'c':
error = addtype(&types,
(flags & LONGINT) ? T_WINT : T_INT);
@@ -364,7 +364,7 @@ reswitch: switch (ch) {
break;
case 'D':
flags |= LONGINT;
- /*FALLTHROUGH*/
+ fallthrough;
case 'd':
case 'i':
if ((error = addsarg(&types, flags)))
@@ -408,7 +408,7 @@ reswitch: switch (ch) {
#endif
case 'O':
flags |= LONGINT;
- /*FALLTHROUGH*/
+ fallthrough;
case 'o':
if ((error = adduarg(&types, flags)))
goto error;
@@ -419,7 +419,7 @@ reswitch: switch (ch) {
break;
case 'S':
flags |= LONGINT;
- /*FALLTHROUGH*/
+ fallthrough;
case 's':
error = addtype(&types,
(flags & LONGINT) ? TP_WCHAR : TP_CHAR);
@@ -428,7 +428,7 @@ reswitch: switch (ch) {
break;
case 'U':
flags |= LONGINT;
- /*FALLTHROUGH*/
+ fallthrough;
case 'u':
case 'X':
case 'x':
@@ -549,7 +549,7 @@ reswitch: switch (ch) {
goto rflag;
case 'C':
flags |= LONGINT;
- /*FALLTHROUGH*/
+ fallthrough;
case 'c':
error = addtype(&types,
(flags & LONGINT) ? T_WINT : T_INT);
@@ -558,7 +558,7 @@ reswitch: switch (ch) {
break;
case 'D':
flags |= LONGINT;
- /*FALLTHROUGH*/
+ fallthrough;
case 'd':
case 'i':
if ((error = addsarg(&types, flags)))
@@ -602,7 +602,7 @@ reswitch: switch (ch) {
#endif
case 'O':
flags |= LONGINT;
- /*FALLTHROUGH*/
+ fallthrough;
case 'o':
if ((error = adduarg(&types, flags)))
goto error;
@@ -613,7 +613,7 @@ reswitch: switch (ch) {
break;
case 'S':
flags |= LONGINT;
- /*FALLTHROUGH*/
+ fallthrough;
case 's':
error = addtype(&types,
(flags & LONGINT) ? TP_WCHAR : TP_CHAR);
@@ -622,7 +622,7 @@ reswitch: switch (ch) {
break;
case 'U':
flags |= LONGINT;
- /*FALLTHROUGH*/
+ fallthrough;
case 'u':
case 'X':
case 'x':
diff --git a/lib/printf/printfcommon.h b/lib/printf/printfcommon.h
index 5c45520b4c..f777be8805 100644
--- a/lib/printf/printfcommon.h
+++ b/lib/printf/printfcommon.h
@@ -8,7 +8,7 @@
* Chris Torek.
*
* Copyright (c) 2011 The FreeBSD Foundation
- * All rights reserved.
+ *
* Portions of this software were developed by David Chisnall
* under sponsorship from the FreeBSD Foundation.
*
@@ -35,8 +35,6 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
- *
- * $FreeBSD$
*/
/*
@@ -70,7 +68,7 @@ io_print(struct io_state *iop, const CHAR * __restrict ptr, size_t len)
{
size_t copylen = len;
- if (!iop->cb)
+ if (!iop->cb || !len)
return 0;
if (iop->avail < copylen)
copylen = iop->avail;
@@ -171,6 +169,13 @@ __ultoa(u_long val, CHAR *endp, int base, int octzero, const char *xdigs)
} while (sval != 0);
break;
+ case 2:
+ do {
+ *--cp = to_char(val & 1);
+ val >>= 1;
+ } while (val);
+ break;
+
case 8:
do {
*--cp = to_char(val & 7);
@@ -221,6 +226,13 @@ __ujtoa(uintmax_t val, CHAR *endp, int base, int octzero, const char *xdigs)
} while (sval != 0);
break;
+ case 2:
+ do {
+ *--cp = to_char(val & 1);
+ val >>= 1;
+ } while (val);
+ break;
+
case 8:
do {
*--cp = to_char(val & 7);
diff --git a/lib/printf/printflocal.h b/lib/printf/printflocal.h
index bac80e801c..93318c8fdb 100644
--- a/lib/printf/printflocal.h
+++ b/lib/printf/printflocal.h
@@ -30,8 +30,6 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
- *
- * $FreeBSD$
*/
#include "compiler.h"
@@ -54,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 cc886834fa..3f6700c838 100644
--- a/lib/printf/vfprintf.c
+++ b/lib/printf/vfprintf.c
@@ -8,7 +8,7 @@
* Chris Torek.
*
* Copyright (c) 2011 The FreeBSD Foundation
- * All rights reserved.
+ *
* Portions of this software were developed by David Chisnall
* under sponsorship from the FreeBSD Foundation.
*
@@ -340,7 +340,7 @@ reswitch: switch (ch) {
if (width >= 0)
goto rflag;
width = -width;
- /* FALLTHROUGH */
+ fallthrough;
case '-':
flags |= LADJUST;
goto rflag;
@@ -416,12 +416,68 @@ 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;
+ case 'B':
+ case 'b':
+ if (flags & INTMAX_SIZE)
+ ujval = UJARG();
+ else
+ ulval = UARG();
+ base = 2;
+ /* leading 0b/B only if non-zero */
+ if (flags & ALT &&
+ (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0))
+ ox[1] = ch;
+ goto nosign;
+ break;
case 'C':
flags |= LONGINT;
- /*FALLTHROUGH*/
+ fallthrough;
case 'c':
#ifdef WCHAR_SUPPORT
if (flags & LONGINT) {
@@ -447,7 +503,7 @@ reswitch: switch (ch) {
break;
case 'D':
flags |= LONGINT;
- /*FALLTHROUGH*/
+ fallthrough;
case 'd':
case 'i':
if (flags & INTMAX_SIZE)
@@ -538,7 +594,7 @@ reswitch: switch (ch) {
break;
case 'O':
flags |= LONGINT;
- /*FALLTHROUGH*/
+ fallthrough;
case 'o':
if (flags & INTMAX_SIZE)
ujval = UJARG();
@@ -582,7 +638,7 @@ reswitch: switch (ch) {
goto nosign;
case 'S':
flags |= LONGINT;
- /*FALLTHROUGH*/
+ fallthrough;
case 's':
#ifdef WCHAR_SUPPORT
if (flags & LONGINT) {
@@ -608,7 +664,7 @@ reswitch: switch (ch) {
break;
case 'U':
flags |= LONGINT;
- /*FALLTHROUGH*/
+ fallthrough;
case 'u':
if (flags & INTMAX_SIZE)
ujval = UJARG();
@@ -671,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/privs.c b/lib/privs.c
index accd9895ff..717a2e48d6 100644
--- a/lib/privs.c
+++ b/lib/privs.c
@@ -6,6 +6,15 @@
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
*/
#include <zebra.h>
+
+#include <pwd.h>
+#include <grp.h>
+
+#ifdef HAVE_LCAPS
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#endif /* HAVE_LCAPS */
+
#include "log.h"
#include "privs.h"
#include "memory.h"
diff --git a/lib/pullwr.c b/lib/pullwr.c
index 3967eb5875..919a663db5 100644
--- a/lib/pullwr.c
+++ b/lib/pullwr.c
@@ -6,6 +6,8 @@
#include "zebra.h"
+#include <sys/ioctl.h>
+
#include "pullwr.h"
#include "memory.h"
#include "monotime.h"
diff --git a/lib/resolver.c b/lib/resolver.c
index 99bf356eb3..d8245e3816 100644
--- a/lib/resolver.c
+++ b/lib/resolver.c
@@ -335,3 +335,8 @@ void resolver_init(struct event_loop *tm)
install_element(CONFIG_NODE, &debug_resolver_cmd);
install_element(ENABLE_NODE, &debug_resolver_cmd);
}
+
+void resolver_terminate(void)
+{
+ ares_destroy(state.channel);
+}
diff --git a/lib/resolver.h b/lib/resolver.h
index 87e8ecdc4a..882f960a45 100644
--- a/lib/resolver.h
+++ b/lib/resolver.h
@@ -23,6 +23,7 @@ struct resolver_query {
};
void resolver_init(struct event_loop *tm);
+void resolver_terminate(void);
void resolver_resolve(struct resolver_query *query, int af, vrf_id_t vrf_id,
const char *hostname,
void (*cb)(struct resolver_query *, const char *, int,
diff --git a/lib/route_types.txt b/lib/route_types.txt
index a82273a6dc..93cbc36e97 100644
--- a/lib/route_types.txt
+++ b/lib/route_types.txt
@@ -49,6 +49,7 @@
ZEBRA_ROUTE_SYSTEM, system, NULL, 'X', 0, 0, 0, "Reserved", none
ZEBRA_ROUTE_KERNEL, kernel, zebra, 'K', 1, 1, 1, "kernel route", zebra
ZEBRA_ROUTE_CONNECT, connected, zebra, 'C', 1, 1, 1, "connected", zebra
+ZEBRA_ROUTE_LOCAL, local, zebra, 'L', 1, 1, 1, "local", zebra
ZEBRA_ROUTE_STATIC, static, zebra, 'S', 1, 1, 1, "static", zebra
ZEBRA_ROUTE_RIP, rip, ripd, 'R', 1, 0, 1, "RIP", ripd
ZEBRA_ROUTE_RIPNG, ripng, ripngd, 'R', 0, 1, 1, "RIPng", ripngd
@@ -86,6 +87,7 @@ ZEBRA_ROUTE_OPENFABRIC, openfabric, fabricd, 'f', 1, 1, 1, "OpenFabric", fa
ZEBRA_ROUTE_VRRP, vrrp, vrrpd, '-', 0, 0, 0, "VRRP", vrrpd
ZEBRA_ROUTE_NHG, zebra, none, '-', 0, 0, 0, "Nexthop Group", none
ZEBRA_ROUTE_SRTE, srte, none, '-', 0, 0, 0, "SR-TE", none
+ZEBRA_ROUTE_TABLE_DIRECT, table-direct, zebra, 't', 1, 1, 1, "Table-Direct", zebra
ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-", none
@@ -93,6 +95,7 @@ ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-", no
ZEBRA_ROUTE_SYSTEM, "Reserved route type, for internal use only"
ZEBRA_ROUTE_KERNEL, "Kernel routes (not installed via the zebra RIB)"
ZEBRA_ROUTE_CONNECT,"Connected routes (directly attached subnet or host)"
+ZEBRA_ROUTE_LOCAL, "Local routes (directly attached host route)"
ZEBRA_ROUTE_STATIC, "Statically configured routes"
ZEBRA_ROUTE_RIP, "Routing Information Protocol (RIP)"
ZEBRA_ROUTE_RIPNG, "Routing Information Protocol next-generation (IPv6) (RIPng)"
@@ -116,3 +119,4 @@ ZEBRA_ROUTE_BFD, "Bidirectional Fowarding Detection (BFD)"
ZEBRA_ROUTE_VRRP, "Virtual Router Redundancy Protocol (VRRP)"
ZEBRA_ROUTE_OPENFABRIC, "OpenFabric Routing Protocol"
ZEBRA_ROUTE_NHG, "Zebra Nexthop Groups (NHG)"
+ZEBRA_ROUTE_TABLE_DIRECT, "Non-main Kernel Routing Table - Direct"
diff --git a/lib/routemap.c b/lib/routemap.c
index 39455841e3..ea917ebd8c 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -1070,20 +1070,17 @@ static int vty_show_route_map(struct vty *vty, const char *name, bool use_json)
{
struct route_map *map;
json_object *json = NULL;
- json_object *json_proto = NULL;
- if (use_json) {
+ if (use_json)
json = json_object_new_object();
- json_proto = json_object_new_object();
- json_object_object_add(json, frr_protonameinst, json_proto);
- } else
+ else
vty_out(vty, "%s:\n", frr_protonameinst);
if (name) {
map = route_map_lookup_by_name(name);
if (map) {
- vty_show_route_map_entry(vty, map, json_proto);
+ vty_show_route_map_entry(vty, map, json);
} else if (!use_json) {
vty_out(vty, "%s: 'route-map %s' not found\n",
frr_protonameinst, name);
@@ -1099,7 +1096,7 @@ static int vty_show_route_map(struct vty *vty, const char *name, bool use_json)
list_sort(maplist, sort_route_map);
for (ALL_LIST_ELEMENTS_RO(maplist, ln, map))
- vty_show_route_map_entry(vty, map, json_proto);
+ vty_show_route_map_entry(vty, map, json);
list_delete(&maplist);
}
@@ -2551,9 +2548,11 @@ route_map_result_t route_map_apply_ext(struct route_map *map,
struct route_map_index *index = NULL;
struct route_map_rule *set = NULL;
bool skip_match_clause = false;
- struct prefix conv;
if (recursion > RMAP_RECURSION_LIMIT) {
+ if (map)
+ map->applied++;
+
flog_warn(
EC_LIB_RMAP_RECURSION_LIMIT,
"route-map recursion limit (%d) reached, discarding route",
@@ -2563,37 +2562,22 @@ route_map_result_t route_map_apply_ext(struct route_map *map,
}
if (map == NULL || map->head == NULL) {
+ if (map)
+ map->applied++;
ret = RMAP_DENYMATCH;
goto route_map_apply_end;
}
map->applied++;
- /*
- * Handling for matching evpn_routes in the prefix table.
- *
- * We convert type2/5 prefix to ipv4/6 prefix to do longest
- * prefix matching on.
- */
if (prefix->family == AF_EVPN) {
- if (evpn_prefix2prefix(prefix, &conv) != 0) {
- if (unlikely(CHECK_FLAG(rmap_debug,
- DEBUG_ROUTEMAP_DETAIL)))
- zlog_debug(
- "Unable to convert EVPN prefix %pFX into IPv4/IPv6 prefix. Falling back to non-optimized route-map lookup",
- prefix);
- } else {
- if (unlikely(CHECK_FLAG(rmap_debug,
- DEBUG_ROUTEMAP_DETAIL)))
- zlog_debug(
- "Converted EVPN prefix %pFX into %pFX for optimized route-map lookup",
- prefix, &conv);
-
- prefix = &conv;
- }
+ index = map->head;
+ } else {
+ skip_match_clause = true;
+ index = route_map_get_index(map, prefix, match_object,
+ &match_ret);
}
- index = route_map_get_index(map, prefix, match_object, &match_ret);
if (index) {
index->applied++;
if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
@@ -2617,7 +2601,6 @@ route_map_result_t route_map_apply_ext(struct route_map *map,
ret = RMAP_DENYMATCH;
goto route_map_apply_end;
}
- skip_match_clause = true;
for (; index; index = index->next) {
if (!skip_match_clause) {
@@ -3140,13 +3123,13 @@ DEFPY (rmap_clear_counters,
}
-DEFUN (rmap_show_name,
- rmap_show_name_cmd,
- "show route-map [WORD] [json]",
- SHOW_STR
- "route-map information\n"
- "route-map name\n"
- JSON_STR)
+DEFUN_NOSH (rmap_show_name,
+ rmap_show_name_cmd,
+ "show route-map [WORD] [json]",
+ SHOW_STR
+ "route-map information\n"
+ "route-map name\n"
+ JSON_STR)
{
bool uj = use_json(argc, argv);
int idx = 0;
@@ -3407,7 +3390,7 @@ DEFUN_HIDDEN(show_route_map_pfx_tbl, show_route_map_pfx_tbl_cmd,
}
/* Initialization of route map vector. */
-void route_map_init(void)
+void route_map_init_new(bool in_backend)
{
int i;
@@ -3422,7 +3405,10 @@ void route_map_init(void)
UNSET_FLAG(rmap_debug, DEBUG_ROUTEMAP);
- route_map_cli_init();
+ if (!in_backend) {
+ /* we do not want to handle config commands in the backend */
+ route_map_cli_init();
+ }
/* Install route map top node. */
install_node(&rmap_debug_node);
@@ -3442,3 +3428,8 @@ void route_map_init(void)
install_element(ENABLE_NODE, &show_route_map_pfx_tbl_cmd);
}
+
+void route_map_init(void)
+{
+ route_map_init_new(false);
+}
diff --git a/lib/routemap.h b/lib/routemap.h
index 770d705837..dfb84ced5b 100644
--- a/lib/routemap.h
+++ b/lib/routemap.h
@@ -259,6 +259,8 @@ DECLARE_QOBJ_TYPE(route_map);
(strmatch(C, "frr-zebra-route-map:ipv4-next-hop-prefix-length"))
#define IS_MATCH_SRC_PROTO(C) \
(strmatch(C, "frr-zebra-route-map:source-protocol"))
+#define IS_MATCH_BGP_SRC_PROTO(C) \
+ (strmatch(C, "frr-bgp-route-map:source-protocol"))
#define IS_MATCH_SRC_INSTANCE(C) \
(strmatch(C, "frr-zebra-route-map:source-instance"))
/* BGP route-map match conditions */
@@ -346,6 +348,8 @@ DECLARE_QOBJ_TYPE(route_map);
(strmatch(A, "frr-bgp-route-map:comm-list-delete"))
#define IS_SET_LCOMM_LIST_DEL(A) \
(strmatch(A, "frr-bgp-route-map:large-comm-list-delete"))
+#define IS_SET_EXTCOMM_LIST_DEL(A) \
+ (strmatch(A, "frr-bgp-route-map:extended-comm-list-delete"))
#define IS_SET_LCOMMUNITY(A) \
(strmatch(A, "frr-bgp-route-map:set-large-community"))
#define IS_SET_COMMUNITY(A) \
@@ -354,10 +358,15 @@ DECLARE_QOBJ_TYPE(route_map);
(strmatch(A, "frr-bgp-route-map:set-extcommunity-none"))
#define IS_SET_EXTCOMMUNITY_RT(A) \
(strmatch(A, "frr-bgp-route-map:set-extcommunity-rt"))
+#define IS_SET_EXTCOMMUNITY_NT(A) \
+ (strmatch(A, "frr-bgp-route-map:set-extcommunity-nt"))
#define IS_SET_EXTCOMMUNITY_SOO(A) \
(strmatch(A, "frr-bgp-route-map:set-extcommunity-soo"))
#define IS_SET_EXTCOMMUNITY_LB(A) \
(strmatch(A, "frr-bgp-route-map:set-extcommunity-lb"))
+#define IS_SET_EXTCOMMUNITY_COLOR(A) \
+ (strmatch(A, "frr-bgp-route-map:set-extcommunity-color"))
+
#define IS_SET_AGGREGATOR(A) \
(strmatch(A, "frr-bgp-route-map:aggregator"))
#define IS_SET_AS_PREPEND(A) \
@@ -392,6 +401,7 @@ enum ecommunity_lb_type {
/* Prototypes. */
extern void route_map_init(void);
+extern void route_map_init_new(bool in_backend);
/*
* This should only be called on shutdown
@@ -1015,6 +1025,7 @@ routemap_hook_context_insert(struct route_map_index *rmi);
void routemap_hook_context_free(struct routemap_hook_context *rhc);
extern const struct frr_yang_module_info frr_route_map_info;
+extern const struct frr_yang_module_info frr_route_map_cli_info;
/* routemap_cli.c */
extern int route_map_instance_cmp(const struct lyd_node *dnode1,
diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c
index 75d1175886..88b341cac0 100644
--- a/lib/routemap_cli.c
+++ b/lib/routemap_cli.c
@@ -90,8 +90,8 @@ DEFPY_YANG(
int route_map_instance_cmp(const struct lyd_node *dnode1,
const struct lyd_node *dnode2)
{
- uint16_t seq1 = yang_dnode_get_uint16(dnode1, "./sequence");
- uint16_t seq2 = yang_dnode_get_uint16(dnode2, "./sequence");
+ uint16_t seq1 = yang_dnode_get_uint16(dnode1, "sequence");
+ uint16_t seq2 = yang_dnode_get_uint16(dnode2, "sequence");
return seq1 - seq2;
}
@@ -100,8 +100,8 @@ void route_map_instance_show(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults)
{
const char *name = yang_dnode_get_string(dnode, "../name");
- const char *action = yang_dnode_get_string(dnode, "./action");
- const char *sequence = yang_dnode_get_string(dnode, "./sequence");
+ const char *action = yang_dnode_get_string(dnode, "action");
+ const char *sequence = yang_dnode_get_string(dnode, "sequence");
vty_out(vty, "route-map %s %s %s\n", name, action, sequence);
@@ -188,7 +188,7 @@ DEFPY_YANG(
DEFPY_YANG(
match_ip_address_prefix_list,
match_ip_address_prefix_list_cmd,
- "match ip address prefix-list PREFIXLIST_NAME$name",
+ "match ip address prefix-list PREFIXLIST4_NAME$name",
MATCH_STR
IP_STR
"Match address of route\n"
@@ -209,7 +209,7 @@ DEFPY_YANG(
DEFPY_YANG(
no_match_ip_address_prefix_list, no_match_ip_address_prefix_list_cmd,
- "no match ip address prefix-list [PREFIXLIST_NAME]",
+ "no match ip address prefix-list [PREFIXLIST4_NAME]",
NO_STR
MATCH_STR
IP_STR
@@ -265,7 +265,7 @@ DEFPY_YANG(
DEFPY_YANG(
match_ip_next_hop_prefix_list,
match_ip_next_hop_prefix_list_cmd,
- "match ip next-hop prefix-list PREFIXLIST_NAME$name",
+ "match ip next-hop prefix-list PREFIXLIST4_NAME$name",
MATCH_STR
IP_STR
"Match next-hop address of route\n"
@@ -287,7 +287,7 @@ DEFPY_YANG(
DEFPY_YANG(
no_match_ip_next_hop_prefix_list,
no_match_ip_next_hop_prefix_list_cmd,
- "no match ip next-hop prefix-list [PREFIXLIST_NAME]",
+ "no match ip next-hop prefix-list [PREFIXLIST4_NAME]",
NO_STR
MATCH_STR
IP_STR
@@ -379,7 +379,7 @@ DEFPY_YANG(
DEFPY_YANG(
match_ipv6_address_prefix_list, match_ipv6_address_prefix_list_cmd,
- "match ipv6 address prefix-list PREFIXLIST_NAME$name",
+ "match ipv6 address prefix-list PREFIXLIST6_NAME$name",
MATCH_STR
IPV6_STR
"Match address of route\n"
@@ -401,7 +401,7 @@ DEFPY_YANG(
DEFPY_YANG(
no_match_ipv6_address_prefix_list,
no_match_ipv6_address_prefix_list_cmd,
- "no match ipv6 address prefix-list [PREFIXLIST_NAME]",
+ "no match ipv6 address prefix-list [PREFIXLIST6_NAME]",
NO_STR
MATCH_STR
IPV6_STR
@@ -526,7 +526,7 @@ DEFPY_YANG(
void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults)
{
- const char *condition = yang_dnode_get_string(dnode, "./condition");
+ const char *condition = yang_dnode_get_string(dnode, "condition");
const struct lyd_node *ln;
const char *acl;
@@ -599,11 +599,14 @@ void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode,
yang_dnode_get_string(
dnode,
"./rmap-match-condition/frr-zebra-route-map:ipv4-prefix-length"));
- } else if (IS_MATCH_SRC_PROTO(condition)) {
+ } else if (IS_MATCH_SRC_PROTO(condition) ||
+ IS_MATCH_BGP_SRC_PROTO(condition)) {
vty_out(vty, " match source-protocol %s\n",
yang_dnode_get_string(
dnode,
- "./rmap-match-condition/frr-zebra-route-map:source-protocol"));
+ IS_MATCH_SRC_PROTO(condition)
+ ? "./rmap-match-condition/frr-zebra-route-map:source-protocol"
+ : "./rmap-match-condition/frr-bgp-route-map:source-protocol"));
} else if (IS_MATCH_SRC_INSTANCE(condition)) {
vty_out(vty, " match source-instance %s\n",
yang_dnode_get_string(
@@ -726,6 +729,10 @@ void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode,
dnode,
"./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match"))
vty_out(vty, " exact-match");
+ if (yang_dnode_get_bool(
+ dnode,
+ "./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-any"))
+ vty_out(vty, " any");
vty_out(vty, "\n");
} else if (IS_MATCH_LCOMMUNITY(condition)) {
vty_out(vty, " match large-community %s",
@@ -736,6 +743,10 @@ void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode,
dnode,
"./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match"))
vty_out(vty, " exact-match");
+ if (yang_dnode_get_bool(
+ dnode,
+ "./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-any"))
+ vty_out(vty, " any");
vty_out(vty, "\n");
} else if (IS_MATCH_EXTCOMMUNITY(condition)) {
vty_out(vty, " match extcommunity %s\n",
@@ -1039,7 +1050,7 @@ DEFUN_YANG (no_set_srte_color,
void route_map_action_show(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults)
{
- const char *action = yang_dnode_get_string(dnode, "./action");
+ const char *action = yang_dnode_get_string(dnode, "action");
const struct lyd_node *ln;
const char *acl;
@@ -1091,7 +1102,7 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *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"));
+ yang_dnode_get_string(dnode, "rmap-set-action/tag"));
} else if (IS_SET_SR_TE_COLOR(action)) {
vty_out(vty, " set sr-te color %s\n",
yang_dnode_get_string(dnode,
@@ -1183,6 +1194,16 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode,
assert(acl);
vty_out(vty, " set large-comm-list %s delete\n", acl);
+ } else if (IS_SET_EXTCOMM_LIST_DEL(action)) {
+ acl = NULL;
+ ln = yang_dnode_get(dnode, "rmap-set-action/frr-bgp-route-map:comm-list-name");
+
+ if (ln)
+ acl = yang_dnode_get_string(ln, NULL);
+
+ assert(acl);
+
+ vty_out(vty, " set extended-comm-list %s delete\n", acl);
} else if (IS_SET_LCOMMUNITY(action)) {
if (yang_dnode_exists(
dnode,
@@ -1218,6 +1239,11 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode,
yang_dnode_get_string(
dnode,
"./rmap-set-action/frr-bgp-route-map:extcommunity-rt"));
+ } else if (IS_SET_EXTCOMMUNITY_NT(action)) {
+ vty_out(vty, " set extcommunity nt %s\n",
+ yang_dnode_get_string(
+ dnode,
+ "./rmap-set-action/frr-bgp-route-map:extcommunity-nt"));
} else if (IS_SET_EXTCOMMUNITY_SOO(action)) {
vty_out(vty, " set extcommunity soo %s\n",
yang_dnode_get_string(
@@ -1251,6 +1277,11 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode,
strlcat(str, " non-transitive", sizeof(str));
vty_out(vty, " set extcommunity bandwidth %s\n", str);
+ } else if (IS_SET_EXTCOMMUNITY_COLOR(action)) {
+ vty_out(vty, " set extcommunity color %s\n",
+ yang_dnode_get_string(
+ dnode,
+ "./rmap-set-action/frr-bgp-route-map:extcommunity-color"));
} else if (IS_SET_EXTCOMMUNITY_NONE(action)) {
if (yang_dnode_get_bool(
dnode,
diff --git a/lib/routemap_northbound.c b/lib/routemap_northbound.c
index 4659850994..1bba4dad47 100644
--- a/lib/routemap_northbound.c
+++ b/lib/routemap_northbound.c
@@ -98,7 +98,7 @@ static int lib_route_map_create(struct nb_cb_create_args *args)
/* NOTHING */
break;
case NB_EV_APPLY:
- rm_name = yang_dnode_get_string(args->dnode, "./name");
+ rm_name = yang_dnode_get_string(args->dnode, "name");
rm = route_map_get(rm_name);
nb_running_set_entry(args->dnode, rm);
break;
@@ -167,8 +167,8 @@ static int lib_route_map_entry_create(struct nb_cb_create_args *args)
/* NOTHING */
break;
case NB_EV_APPLY:
- sequence = yang_dnode_get_uint16(args->dnode, "./sequence");
- action = yang_dnode_get_enum(args->dnode, "./action") == 0
+ sequence = yang_dnode_get_uint16(args->dnode, "sequence");
+ action = yang_dnode_get_enum(args->dnode, "action") == 0
? RMAP_PERMIT
: RMAP_DENY;
rm = nb_running_get_entry(args->dnode, NULL, true);
@@ -353,6 +353,7 @@ static int
lib_route_map_entry_exit_policy_modify(struct nb_cb_modify_args *args)
{
struct route_map_index *rmi;
+ struct route_map *map;
int rm_action;
int policy;
@@ -363,7 +364,6 @@ lib_route_map_entry_exit_policy_modify(struct nb_cb_modify_args *args)
case 0: /* permit-or-deny */
break;
case 1: /* next */
- /* FALLTHROUGH */
case 2: /* goto */
rm_action =
yang_dnode_get_enum(args->dnode, "../action");
@@ -382,6 +382,7 @@ lib_route_map_entry_exit_policy_modify(struct nb_cb_modify_args *args)
break;
case NB_EV_APPLY:
rmi = nb_running_get_entry(args->dnode, NULL, true);
+ map = rmi->map;
policy = yang_dnode_get_enum(args->dnode, NULL);
switch (policy) {
@@ -395,6 +396,14 @@ lib_route_map_entry_exit_policy_modify(struct nb_cb_modify_args *args)
rmi->exitpolicy = RMAP_GOTO;
break;
}
+
+ /* Execute event hook. */
+ if (route_map_master.event_hook) {
+ (*route_map_master.event_hook)(map->name);
+ route_map_notify_dependencies(map->name,
+ RMAP_EVENT_CALL_ADDED);
+ }
+
break;
}
@@ -875,7 +884,7 @@ static int lib_route_map_entry_set_action_ipv4_address_modify(
yang_dnode_get_ipv4(&ia, args->dnode, NULL);
if (ia.s_addr == INADDR_ANY || !ipv4_unicast_valid(&ia))
return NB_ERR_VALIDATION;
- /* FALLTHROUGH */
+ return NB_OK;
case NB_EV_PREPARE:
case NB_EV_ABORT:
return NB_OK;
@@ -934,7 +943,7 @@ static int lib_route_map_entry_set_action_ipv6_address_modify(
yang_dnode_get_ipv6(&i6a, args->dnode, NULL);
if (!IN6_IS_ADDR_LINKLOCAL(&i6a))
return NB_ERR_VALIDATION;
- /* FALLTHROUGH */
+ return NB_OK;
case NB_EV_PREPARE:
case NB_EV_ABORT:
return NB_OK;
@@ -1541,3 +1550,45 @@ const struct frr_yang_module_info frr_route_map_info = {
},
}
};
+
+const struct frr_yang_module_info frr_route_map_cli_info = {
+ .name = "frr-route-map",
+ .ignore_cfg_cbs = true,
+ .nodes = {
+ {
+ .xpath = "/frr-route-map:lib/route-map/optimization-disabled",
+ .cbs.cli_show = route_map_optimization_disabled_show,
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry",
+ .cbs = {
+ .cli_cmp = route_map_instance_cmp,
+ .cli_show = route_map_instance_show,
+ .cli_show_end = route_map_instance_show_end,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/description",
+ .cbs.cli_show = route_map_description_show,
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/call",
+ .cbs.cli_show = route_map_call_show,
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/exit-policy",
+ .cbs.cli_show = route_map_exit_policy_show,
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition",
+ .cbs.cli_show = route_map_condition_show,
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action",
+ .cbs.cli_show = route_map_action_show,
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
diff --git a/lib/routing_nb.c b/lib/routing_nb.c
index 3d837bcc11..33372d113a 100644
--- a/lib/routing_nb.c
+++ b/lib/routing_nb.c
@@ -27,3 +27,13 @@ const struct frr_yang_module_info frr_routing_info = {
},
}
};
+
+const struct frr_yang_module_info frr_routing_cli_info = {
+ .name = "frr-routing",
+ .ignore_cfg_cbs = true,
+ .nodes = {
+ {
+ .xpath = NULL,
+ },
+ }
+};
diff --git a/lib/routing_nb.h b/lib/routing_nb.h
index c185091a4b..26b4cf08d8 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_
@@ -6,6 +8,7 @@ extern "C" {
#endif
extern const struct frr_yang_module_info frr_routing_info;
+extern const struct frr_yang_module_info frr_routing_cli_info;
/* Mandatory callbacks. */
int routing_control_plane_protocols_control_plane_protocol_create(
@@ -29,6 +32,8 @@ int routing_control_plane_protocols_control_plane_protocol_destroy(
* based on the control plane protocol
*/
DECLARE_HOOK(routing_conf_event, (struct nb_cb_create_args *args), (args));
+DECLARE_HOOK(routing_create, (struct nb_cb_create_args *args), (args));
+DECLARE_KOOH(routing_destroy, (struct nb_cb_destroy_args *args), (args));
void routing_control_plane_protocols_register_vrf_dependency(void);
diff --git a/lib/routing_nb_config.c b/lib/routing_nb_config.c
index f4fe48f5b3..d532279a22 100644
--- a/lib/routing_nb_config.c
+++ b/lib/routing_nb_config.c
@@ -14,6 +14,8 @@
DEFINE_HOOK(routing_conf_event, (struct nb_cb_create_args *args), (args));
+DEFINE_HOOK(routing_create, (struct nb_cb_create_args *args), (args));
+DEFINE_KOOH(routing_destroy, (struct nb_cb_destroy_args *args), (args));
/*
* XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol
@@ -44,11 +46,12 @@ int routing_control_plane_protocols_control_plane_protocol_create(
* find the vrf and store the pointer.
*/
if (nb_node_has_dependency(args->dnode->schema->priv)) {
- vrfname = yang_dnode_get_string(args->dnode, "./vrf");
+ vrfname = yang_dnode_get_string(args->dnode, "vrf");
vrf = vrf_lookup_by_name(vrfname);
assert(vrf);
nb_running_set_entry(args->dnode, vrf);
}
+ hook_call(routing_create, args);
break;
};
@@ -61,6 +64,8 @@ int routing_control_plane_protocols_control_plane_protocol_destroy(
if (args->event != NB_EV_APPLY)
return NB_OK;
+ hook_call(routing_destroy, args);
+
/*
* If dependency on VRF module is registered, then VRF
* pointer was stored and must be cleared.
@@ -76,7 +81,7 @@ static void vrf_to_control_plane_protocol(const struct lyd_node *dnode,
{
const char *vrf;
- vrf = yang_dnode_get_string(dnode, "./name");
+ vrf = yang_dnode_get_string(dnode, "name");
snprintf(xpath, XPATH_MAXLEN, FRR_ROUTING_KEY_XPATH_VRF, vrf);
}
@@ -86,7 +91,7 @@ static void control_plane_protocol_to_vrf(const struct lyd_node *dnode,
{
const char *vrf;
- vrf = yang_dnode_get_string(dnode, "./vrf");
+ vrf = yang_dnode_get_string(dnode, "vrf");
snprintf(xpath, XPATH_MAXLEN, FRR_VRF_KEY_XPATH, vrf);
}
diff --git a/lib/sha256.c b/lib/sha256.c
index ccf260fa7b..08e08eb06b 100644
--- a/lib/sha256.c
+++ b/lib/sha256.c
@@ -344,7 +344,7 @@ void HMAC__SHA256_Final(unsigned char digest[32], HMAC_SHA256_CTX *ctx)
void PBKDF2_SHA256(const uint8_t *passwd, size_t passwdlen, const uint8_t *salt,
size_t saltlen, uint64_t c, uint8_t *buf, size_t dkLen)
{
- HMAC_SHA256_CTX PShctx, hctx;
+ HMAC_SHA256_CTX PShctx = {}, hctx;
size_t i;
uint8_t ivec[4];
uint8_t U[32];
diff --git a/lib/sigevent.c b/lib/sigevent.c
index 3cd65eb800..3e69f280da 100644
--- a/lib/sigevent.c
+++ b/lib/sigevent.c
@@ -4,6 +4,8 @@
*/
#include <zebra.h>
+
+#include <signal.h>
#include <sigevent.h>
#include <log.h>
#include <memory.h>
@@ -238,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/sockopt.c b/lib/sockopt.c
index 0c4adb0b7c..74bc034ccd 100644
--- a/lib/sockopt.c
+++ b/lib/sockopt.c
@@ -10,13 +10,12 @@
#include "sockunion.h"
#include "lib_errors.h"
-#if (defined(__FreeBSD__) \
- && ((__FreeBSD_version >= 500022 && __FreeBSD_version < 700000) \
- || (__FreeBSD_version < 500000 && __FreeBSD_version >= 440000))) \
- || (defined(__NetBSD__) && defined(__NetBSD_Version__) \
- && __NetBSD_Version__ >= 106010000) \
- || defined(__OpenBSD__) || defined(__APPLE__) \
- || defined(__DragonFly__) || defined(__sun)
+#if (defined(__FreeBSD__) && \
+ ((__FreeBSD_version >= 500022 && __FreeBSD_version < 700000) || \
+ (__FreeBSD_version < 500000 && __FreeBSD_version >= 440000))) || \
+ (defined(__NetBSD__) && defined(__NetBSD_Version__) && \
+ __NetBSD_Version__ >= 106010000) || \
+ defined(__OpenBSD__) || defined(__DragonFly__) || defined(__sun)
#define HAVE_BSD_STRUCT_IP_MREQ_HACK
#endif
@@ -24,9 +23,12 @@ void setsockopt_so_recvbuf(int sock, int size)
{
int orig_req = size;
- while (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size))
- == -1)
+ while (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) ==
+ -1) {
+ if (size == 0)
+ break;
size /= 2;
+ }
if (size != orig_req)
flog_err(EC_LIB_SOCKET,
@@ -38,9 +40,12 @@ void setsockopt_so_sendbuf(const int sock, int size)
{
int orig_req = size;
- while (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size))
- == -1)
+ while (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) ==
+ -1) {
+ if (size == 0)
+ break;
size /= 2;
+ }
if (size != orig_req)
flog_err(EC_LIB_SOCKET,
@@ -667,6 +672,9 @@ int sockopt_tcp_mss_get(int sock)
int tcp_maxseg = 0;
socklen_t tcp_maxseg_len = sizeof(tcp_maxseg);
+ if (sock < 0)
+ return 0;
+
ret = getsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, &tcp_maxseg,
&tcp_maxseg_len);
if (ret != 0) {
diff --git a/lib/sockunion.h b/lib/sockunion.h
index e507255999..146651225c 100644
--- a/lib/sockunion.h
+++ b/lib/sockunion.h
@@ -7,10 +7,13 @@
#ifndef _ZEBRA_SOCKUNION_H
#define _ZEBRA_SOCKUNION_H
+#include "compiler.h"
+
#include "privs.h"
#include "if.h"
#include <sys/un.h>
#ifdef __OpenBSD__
+#include <net/route.h>
#include <netmpls/mpls.h>
#endif
@@ -27,8 +30,40 @@ union sockunion {
struct sockaddr_mpls smpls;
struct sockaddr_rtlabel rtlabel;
#endif
+
+ /* sockaddr_storage is guaranteed to be larger than the others */
+ struct sockaddr_storage sa_storage;
};
+/* clang-format off */
+/* for functions that want to accept any sockaddr pointer without casts */
+union sockaddrptr {
+ uniontype(sockaddrptr, union sockunion, su)
+ uniontype(sockaddrptr, struct sockaddr, sa)
+ uniontype(sockaddrptr, struct sockaddr_in, sin)
+ uniontype(sockaddrptr, struct sockaddr_in6, sin6)
+ uniontype(sockaddrptr, struct sockaddr_un, sun)
+#ifdef __OpenBSD__
+ uniontype(sockaddrptr, struct sockaddr_mpls, smpls)
+ uniontype(sockaddrptr, struct sockaddr_rtlabel, rtlabel)
+#endif
+ uniontype(sockaddrptr, struct sockaddr_storage, sa_storage)
+} TRANSPARENT_UNION;
+
+union sockaddrconstptr {
+ uniontype(sockaddrconstptr, const union sockunion, su)
+ uniontype(sockaddrconstptr, const struct sockaddr, sa)
+ uniontype(sockaddrconstptr, const struct sockaddr_in, sin)
+ uniontype(sockaddrconstptr, const struct sockaddr_in6, sin6)
+ uniontype(sockaddrconstptr, const struct sockaddr_un, sun)
+#ifdef __OpenBSD__
+ uniontype(sockaddrconstptr, const struct sockaddr_mpls, smpls)
+ uniontype(sockaddrconstptr, const struct sockaddr_rtlabel, rtlabel)
+#endif
+ uniontype(sockaddrconstptr, const struct sockaddr_storage, sa_storage)
+} TRANSPARENT_UNION;
+/* clang-format on */
+
enum connect_result { connect_error, connect_success, connect_in_progress };
/* Default address family. */
diff --git a/lib/srv6.c b/lib/srv6.c
index 09835f3ea8..a82103e423 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:
@@ -108,7 +110,7 @@ const char *seg6local_context2str(char *str, size_t size,
}
}
-static void srv6_locator_chunk_list_free(void *data)
+void srv6_locator_chunk_list_free(void *data)
{
struct srv6_locator_chunk *chunk = data;
diff --git a/lib/srv6.h b/lib/srv6.h
index 0d3ee7d2ff..433c5c14fd 100644
--- a/lib/srv6.h
+++ b/lib/srv6.h
@@ -14,8 +14,11 @@
#include <arpa/inet.h>
#include <netinet/in.h>
-#define SRV6_MAX_SIDS 16
+#define SRV6_MAX_SIDS 16
+#define SRV6_MAX_SEGS 8
#define SRV6_LOCNAME_SIZE 256
+#define SRH_BASE_HEADER_LENGTH 8
+#define SRH_SEGMENT_LENGTH 16
#ifdef __cplusplus
extern "C" {
@@ -24,6 +27,16 @@ extern "C" {
#define sid2str(sid, str, size) \
inet_ntop(AF_INET6, sid, str, size)
+/* SRv6 flavors manipulation macros */
+#define CHECK_SRV6_FLV_OP(OPS,OP) ((OPS) & (1 << OP))
+#define SET_SRV6_FLV_OP(OPS,OP) (OPS) |= (1 << OP)
+#define UNSET_SRV6_FLV_OP(OPS,OP) (OPS) &= ~(1 << OP)
+#define RESET_SRV6_FLV_OP(OPS) (OPS) = 0
+
+/* SRv6 Flavors default values */
+#define ZEBRA_DEFAULT_SEG6_LOCAL_FLV_LCBLOCK_LEN 32
+#define ZEBRA_DEFAULT_SEG6_LOCAL_FLV_LCNODE_FN_LEN 16
+
enum seg6_mode_t {
INLINE,
ENCAP,
@@ -50,15 +63,47 @@ enum seg6local_action_t {
ZEBRA_SEG6_LOCAL_ACTION_END_DT46 = 16,
};
+/* Flavor operations for SRv6 End* Behaviors */
+enum seg6local_flavor_op {
+ ZEBRA_SEG6_LOCAL_FLV_OP_UNSPEC = 0,
+ /* PSP Flavor as per RFC 8986 section #4.16.1 */
+ ZEBRA_SEG6_LOCAL_FLV_OP_PSP = 1,
+ /* USP Flavor as per RFC 8986 section #4.16.2 */
+ ZEBRA_SEG6_LOCAL_FLV_OP_USP = 2,
+ /* USD Flavor as per RFC 8986 section #4.16.3 */
+ ZEBRA_SEG6_LOCAL_FLV_OP_USD = 3,
+ /* NEXT-C-SID Flavor as per draft-ietf-spring-srv6-srh-compression-03
+ section 4.1 */
+ ZEBRA_SEG6_LOCAL_FLV_OP_NEXT_CSID = 4,
+};
+
+#define SRV6_SEG_STRLEN 1024
+
struct seg6_segs {
size_t num_segs;
struct in6_addr segs[256];
};
+struct seg6local_flavors_info {
+ /* Flavor operations */
+ uint32_t flv_ops;
+
+ /* Locator-Block length, expressed in bits */
+ uint8_t lcblock_len;
+ /* Locator-Node Function length, expressed in bits */
+ uint8_t lcnode_func_len;
+};
+
+struct seg6_seg_stack {
+ uint8_t num_segs;
+ struct in6_addr seg[0]; /* 1 or more segs */
+};
+
struct seg6local_context {
struct in_addr nh4;
struct in6_addr nh6;
uint32_t table;
+ struct seg6local_flavors_info flv;
};
struct srv6_locator {
@@ -115,14 +160,18 @@ struct srv6_locator_chunk {
* https://www.iana.org/assignments/segment-routing/segment-routing.xhtml
*/
enum srv6_endpoint_behavior_codepoint {
- SRV6_ENDPOINT_BEHAVIOR_RESERVED = 0x0000,
- SRV6_ENDPOINT_BEHAVIOR_END_DT6 = 0x0012,
- SRV6_ENDPOINT_BEHAVIOR_END_DT4 = 0x0013,
- SRV6_ENDPOINT_BEHAVIOR_END_DT46 = 0x0014,
- SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID = 0x003E,
- SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID = 0x003F,
- SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID = 0x0040,
- SRV6_ENDPOINT_BEHAVIOR_OPAQUE = 0xFFFF,
+ SRV6_ENDPOINT_BEHAVIOR_RESERVED = 0x0000,
+ SRV6_ENDPOINT_BEHAVIOR_END = 0x0001,
+ SRV6_ENDPOINT_BEHAVIOR_END_X = 0x0005,
+ SRV6_ENDPOINT_BEHAVIOR_END_DT6 = 0x0012,
+ SRV6_ENDPOINT_BEHAVIOR_END_DT4 = 0x0013,
+ SRV6_ENDPOINT_BEHAVIOR_END_DT46 = 0x0014,
+ SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID = 0x002B,
+ SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID = 0x002C,
+ SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID = 0x003E,
+ SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID = 0x003F,
+ SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID = 0x0040,
+ SRV6_ENDPOINT_BEHAVIOR_OPAQUE = 0xFFFF,
};
struct nexthop_srv6 {
@@ -131,7 +180,7 @@ struct nexthop_srv6 {
struct seg6local_context seg6local_ctx;
/* SRv6 Headend-behaviour */
- struct in6_addr seg6_segs;
+ struct seg6_seg_stack *seg6_segs;
};
static inline const char *seg6_mode2str(enum seg6_mode_t mode)
@@ -167,12 +216,21 @@ static inline bool sid_diff(
return !sid_same(a, b);
}
-static inline bool sid_zero(
- const struct in6_addr *a)
+
+static inline bool sid_zero(const struct seg6_seg_stack *a)
+{
+ struct in6_addr zero = {};
+
+ assert(a);
+
+ return sid_same(&a->seg[0], &zero);
+}
+
+static inline bool sid_zero_ipv6(const struct in6_addr *a)
{
struct in6_addr zero = {};
- return sid_same(a, &zero);
+ return sid_same(&a[0], &zero);
}
static inline void *sid_copy(struct in6_addr *dst,
@@ -194,6 +252,7 @@ int snprintf_seg6_segs(char *str,
extern struct srv6_locator *srv6_locator_alloc(const char *name);
extern struct srv6_locator_chunk *srv6_locator_chunk_alloc(void);
extern void srv6_locator_free(struct srv6_locator *locator);
+extern void srv6_locator_chunk_list_free(void *data);
extern void srv6_locator_chunk_free(struct srv6_locator_chunk **chunk);
json_object *srv6_locator_chunk_json(const struct srv6_locator_chunk *chunk);
json_object *srv6_locator_json(const struct srv6_locator *loc);
diff --git a/lib/stream.h b/lib/stream.h
index 85eebb47be..61eaa46c95 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;
diff --git a/lib/subdir.am b/lib/subdir.am
index 0cd68c0e70..5ec6adf4c0 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -25,6 +25,7 @@ lib_libfrr_la_SOURCES = \
lib/command_parse.y \
lib/cspf.c \
lib/csv.c \
+ lib/darr.c \
lib/debug.c \
lib/defaults.c \
lib/distribute.c \
@@ -52,6 +53,8 @@ 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 \
@@ -67,6 +70,7 @@ lib_libfrr_la_SOURCES = \
lib/mgmt_be_client.c \
lib/mgmt_fe_client.c \
lib/mgmt_msg.c \
+ lib/mgmt_msg_native.c \
lib/mlag.c \
lib/module.c \
lib/mpls.c \
@@ -79,6 +83,7 @@ lib_libfrr_la_SOURCES = \
lib/northbound.c \
lib/northbound_cli.c \
lib/northbound_db.c \
+ lib/northbound_oper.c \
lib/ntop.c \
lib/openbsd-tree.c \
lib/pid_output.c \
@@ -126,6 +131,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 \
@@ -138,13 +144,16 @@ lib_libfrr_la_SOURCES = \
nodist_lib_libfrr_la_SOURCES = \
yang/frr-affinity-map.yang.c \
yang/frr-filter.yang.c \
+ yang/frr-if-rmap.yang.c \
yang/frr-interface.yang.c \
yang/frr-route-map.yang.c \
yang/frr-route-types.yang.c \
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-key-chain.yang.c \
yang/ietf/ietf-interfaces.yang.c \
yang/ietf/ietf-bgp-types.yang.c \
yang/frr-module-translator.yang.c \
@@ -177,7 +186,10 @@ 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 \
lib/nexthop_group.c \
lib/northbound_cli.c \
lib/plist.c \
@@ -206,6 +218,7 @@ pkginclude_HEADERS += \
lib/compiler.h \
lib/cspf.h \
lib/csv.h \
+ lib/darr.h \
lib/db.h \
lib/debug.h \
lib/defaults.h \
@@ -214,11 +227,13 @@ pkginclude_HEADERS += \
lib/filter.h \
lib/flex_algo.h \
lib/freebsd-queue.h \
+ lib/frrdistance.h \
lib/frrlua.h \
lib/frrscript.h \
lib/frr_pthread.h \
lib/frratomic.h \
lib/frrcu.h \
+ lib/frrsendmmsg.h \
lib/frrstr.h \
lib/graph.h \
lib/hash.h \
@@ -247,8 +262,10 @@ pkginclude_HEADERS += \
lib/memory.h \
lib/mgmt.pb-c.h \
lib/mgmt_be_client.h \
+ lib/mgmt_defines.h \
lib/mgmt_fe_client.h \
lib/mgmt_msg.h \
+ lib/mgmt_msg_native.h \
lib/mgmt_pb.h \
lib/module.h \
lib/monotime.h \
@@ -313,6 +330,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 \
@@ -392,18 +410,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
@@ -529,12 +535,22 @@ EXTRA_DIST += \
BUILT_SOURCES += \
lib/gitversion.h \
lib/route_types.h \
+ lib/vtysh_daemons.h \
# end
## force route_types.h
$(lib_clippy_OBJECTS): lib/route_types.h
$(lib_libfrr_la_OBJECTS): lib/route_types.h
+# force lib_daemons.h
+$(lib_libfrr_la_OBJECTS): lib/vtysh_daemons.h
+
+CLEANFILES += lib/vtysh_daemons.h
+lib/vtysh_daemons.h:
+ @$(MKDIR_P) lib
+ $(PERL) $(top_srcdir)/vtysh/daemons.pl $(vtysh_daemons) > lib/vtysh_daemons.h
+
+
AM_YFLAGS = -d -Dapi.prefix=@BISON_OPENBRACE@cmd_yy@BISON_CLOSEBRACE@ @BISON_VERBOSE@
lib/command_lex.h: lib/command_lex.c
diff --git a/lib/systemd.c b/lib/systemd.c
index 56a53a6e78..a82c376cfd 100644
--- a/lib/systemd.c
+++ b/lib/systemd.c
@@ -5,6 +5,7 @@
*/
#include <zebra.h>
+#include <sys/stat.h>
#include <sys/un.h>
#include "frrevent.h"
diff --git a/lib/table.c b/lib/table.c
index 380852b54d..3bf93894ec 100644
--- a/lib/table.c
+++ b/lib/table.c
@@ -334,7 +334,6 @@ void route_node_delete(struct route_node *node)
struct route_node *parent;
assert(node->lock == 0);
- assert(node->info == NULL);
if (node->l_left && node->l_right)
return;
diff --git a/lib/typesafe.h b/lib/typesafe.h
index 8eb59c33b7..93258c5954 100644
--- a/lib/typesafe.h
+++ b/lib/typesafe.h
@@ -17,12 +17,14 @@ extern "C" {
/* generic macros for all list-like types */
+/* to iterate using the const variants of the functions, append "_const" to
+ * the name of the container, e.g. "frr_each (my_list, head, item)" becomes
+ * "frr_each (my_list_const, head, item)"
+ */
+
#define frr_each(prefix, head, item) \
for (item = prefix##_first(head); item; \
item = prefix##_next(head, item))
-#define frr_each_const(prefix, head, item) \
- for (item = prefix##_const_first(head); item; \
- item = prefix##_const_next(head, item))
#define frr_each_safe(prefix, head, item) \
for (typeof(prefix##_next_safe(head, NULL)) prefix##_safe = \
prefix##_next_safe(head, \
@@ -793,13 +795,16 @@ struct thash_head {
uint8_t minshift, maxshift;
};
-#define _HASH_SIZE(tabshift) \
- ((1U << (tabshift)) >> 1)
+#define _HASH_SIZE(tabshift) \
+ ({ \
+ assume((tabshift) <= 31); \
+ (1U << (tabshift)) >> 1; \
+ })
#define HASH_SIZE(head) \
_HASH_SIZE((head).tabshift)
#define _HASH_KEY(tabshift, val) \
({ \
- assume((tabshift) >= 2 && (tabshift) <= 33); \
+ assume((tabshift) >= 2 && (tabshift) <= 31); \
(val) >> (33 - (tabshift)); \
})
#define HASH_KEY(head, val) \
diff --git a/lib/vector.c b/lib/vector.c
index bbea67c12f..60d383101a 100644
--- a/lib/vector.c
+++ b/lib/vector.c
@@ -4,6 +4,7 @@
*/
#include <zebra.h>
+#include <string.h>
#include "vector.h"
#include "memory.h"
diff --git a/lib/vrf.c b/lib/vrf.c
index 73f5d8ff72..e907626bae 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -5,6 +5,7 @@
*/
#include <zebra.h>
+#include <sys/ioctl.h>
#include "if.h"
#include "vrf.h"
@@ -325,6 +326,33 @@ void vrf_disable(struct vrf *vrf)
(*vrf_master.vrf_disable_hook)(vrf);
}
+void vrf_iterate(vrf_iter_func fnc)
+{
+ struct vrf *vrf, *tmp;
+
+ if (debug_vrf)
+ zlog_debug("%s: vrf subsystem iteration", __func__);
+
+ RB_FOREACH_SAFE (vrf, vrf_id_head, &vrfs_by_id, tmp) {
+ if (vrf->vrf_id == VRF_DEFAULT)
+ continue;
+
+ fnc(vrf);
+ }
+
+ RB_FOREACH_SAFE (vrf, vrf_name_head, &vrfs_by_name, tmp) {
+ if (vrf->vrf_id == VRF_DEFAULT)
+ continue;
+
+ fnc(vrf);
+ }
+
+ /* Finally process default VRF */
+ vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ if (vrf)
+ fnc(vrf);
+}
+
const char *vrf_id_to_name(vrf_id_t vrf_id)
{
struct vrf *vrf;
@@ -384,54 +412,80 @@ static void vrf_hash_bitmap_free(void *data)
XFREE(MTYPE_VRF_BITMAP, bit);
}
-vrf_bitmap_t vrf_bitmap_init(void)
+void vrf_bitmap_init(vrf_bitmap_t *pbmap)
{
- return hash_create_size(32, vrf_hash_bitmap_key, vrf_hash_bitmap_cmp,
- "VRF BIT HASH");
+ *pbmap = NULL;
}
-void vrf_bitmap_free(vrf_bitmap_t bmap)
+void vrf_bitmap_free(vrf_bitmap_t *pbmap)
{
- struct hash *vrf_hash = bmap;
+ struct hash *vrf_hash;
+
+ if (!*pbmap)
+ return;
+
+ vrf_hash = *pbmap;
hash_clean_and_free(&vrf_hash, vrf_hash_bitmap_free);
}
-void vrf_bitmap_set(vrf_bitmap_t bmap, vrf_id_t vrf_id)
+void vrf_bitmap_set(vrf_bitmap_t *pbmap, vrf_id_t vrf_id)
{
struct vrf_bit_set lookup = { .vrf_id = vrf_id };
- struct hash *vrf_hash = bmap;
+ struct hash *vrf_hash;
struct vrf_bit_set *bit;
- if (vrf_hash == NULL || vrf_id == VRF_UNKNOWN)
+ if (vrf_id == VRF_UNKNOWN)
return;
+ if (!*pbmap)
+ *pbmap = vrf_hash =
+ hash_create_size(2, vrf_hash_bitmap_key,
+ vrf_hash_bitmap_cmp, "VRF BIT HASH");
+ else
+ vrf_hash = *pbmap;
+
bit = hash_get(vrf_hash, &lookup, vrf_hash_bitmap_alloc);
bit->set = true;
}
-void vrf_bitmap_unset(vrf_bitmap_t bmap, vrf_id_t vrf_id)
+void vrf_bitmap_unset(vrf_bitmap_t *pbmap, vrf_id_t vrf_id)
{
struct vrf_bit_set lookup = { .vrf_id = vrf_id };
- struct hash *vrf_hash = bmap;
+ struct hash *vrf_hash;
struct vrf_bit_set *bit;
- if (vrf_hash == NULL || vrf_id == VRF_UNKNOWN)
+ if (vrf_id == VRF_UNKNOWN)
+ return;
+
+ /*
+ * If the hash is not created then unsetting is unnecessary
+ */
+ if (!*pbmap)
+ return;
+
+ vrf_hash = *pbmap;
+
+ /*
+ * If we can't look it up, no need to unset it!
+ */
+ bit = hash_lookup(vrf_hash, &lookup);
+ if (!bit)
return;
- bit = hash_get(vrf_hash, &lookup, vrf_hash_bitmap_alloc);
bit->set = false;
}
-int vrf_bitmap_check(vrf_bitmap_t bmap, vrf_id_t vrf_id)
+int vrf_bitmap_check(vrf_bitmap_t *pbmap, vrf_id_t vrf_id)
{
struct vrf_bit_set lookup = { .vrf_id = vrf_id };
- struct hash *vrf_hash = bmap;
+ struct hash *vrf_hash;
struct vrf_bit_set *bit;
- if (vrf_hash == NULL || vrf_id == VRF_UNKNOWN)
+ if (!*pbmap || vrf_id == VRF_UNKNOWN)
return 0;
+ vrf_hash = *pbmap;
bit = hash_lookup(vrf_hash, &lookup);
if (bit)
return bit->set;
@@ -515,32 +569,12 @@ static void vrf_terminate_single(struct vrf *vrf)
vrf_delete(vrf);
}
-/* Terminate VRF module. */
void vrf_terminate(void)
{
- struct vrf *vrf, *tmp;
-
if (debug_vrf)
zlog_debug("%s: Shutting down vrf subsystem", __func__);
- RB_FOREACH_SAFE (vrf, vrf_id_head, &vrfs_by_id, tmp) {
- if (vrf->vrf_id == VRF_DEFAULT)
- continue;
-
- vrf_terminate_single(vrf);
- }
-
- RB_FOREACH_SAFE (vrf, vrf_name_head, &vrfs_by_name, tmp) {
- if (vrf->vrf_id == VRF_DEFAULT)
- continue;
-
- vrf_terminate_single(vrf);
- }
-
- /* Finally terminate default VRF */
- vrf = vrf_lookup_by_id(VRF_DEFAULT);
- if (vrf)
- vrf_terminate_single(vrf);
+ vrf_iterate(vrf_terminate_single);
}
int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id,
@@ -654,18 +688,6 @@ DEFUN_YANG (no_vrf,
const char *vrfname = argv[2]->arg;
char xpath_list[XPATH_MAXLEN];
- struct vrf *vrfp;
-
- vrfp = vrf_lookup_by_name(vrfname);
-
- if (vrfp == NULL)
- return CMD_SUCCESS;
-
- if (CHECK_FLAG(vrfp->status, VRF_ACTIVE)) {
- vty_out(vty, "%% Only inactive VRFs can be deleted\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
if (vrf_get_backend() == VRF_BACKEND_VRF_LITE) {
/*
* Remove the VRF interface config when removing the VRF.
@@ -888,7 +910,7 @@ static int lib_vrf_create(struct nb_cb_create_args *args)
const char *vrfname;
struct vrf *vrfp;
- vrfname = yang_dnode_get_string(args->dnode, "./name");
+ vrfname = yang_dnode_get_string(args->dnode, "name");
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -929,6 +951,25 @@ static int lib_vrf_destroy(struct nb_cb_destroy_args *args)
return NB_OK;
}
+static void lib_vrf_cli_write(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *name = yang_dnode_get_string(dnode, "name");
+
+ if (strcmp(name, VRF_DEFAULT_NAME)) {
+ vty_out(vty, "!\n");
+ vty_out(vty, "vrf %s\n", name);
+ }
+}
+
+static void lib_vrf_cli_write_end(struct vty *vty, const struct lyd_node *dnode)
+{
+ const char *name = yang_dnode_get_string(dnode, "name");
+
+ if (strcmp(name, VRF_DEFAULT_NAME))
+ vty_out(vty, "exit-vrf\n");
+}
+
static const void *lib_vrf_get_next(struct nb_cb_get_next_args *args)
{
struct vrf *vrfp = (struct vrf *)args->list_entry;
@@ -961,6 +1002,19 @@ static const void *lib_vrf_lookup_entry(struct nb_cb_lookup_entry_args *args)
return vrf;
}
+static const void *lib_vrf_lookup_next(struct nb_cb_lookup_entry_args *args)
+{
+ const char *vrfname = args->keys->key[0];
+ struct vrf vrfkey, *vrf;
+
+ strlcpy(vrfkey.name, vrfname, sizeof(vrfkey.name));
+ vrf = RB_FIND(vrf_name_head, &vrfs_by_name, &vrfkey);
+ if (!strcmp(vrf->name, vrfname))
+ vrf = RB_NEXT(vrf_name_head, vrf);
+
+ return vrf;
+}
+
/*
* XPath: /frr-vrf:lib/vrf/id
*/
@@ -987,6 +1041,8 @@ lib_vrf_state_active_get_elem(struct nb_cb_get_elem_args *args)
}
/* clang-format off */
+
+/* cli_show callbacks are kept here for daemons not yet converted to mgmtd */
const struct frr_yang_module_info frr_vrf_info = {
.name = "frr-vrf",
.nodes = {
@@ -995,9 +1051,12 @@ const struct frr_yang_module_info frr_vrf_info = {
.cbs = {
.create = lib_vrf_create,
.destroy = lib_vrf_destroy,
+ .cli_show = lib_vrf_cli_write,
+ .cli_show_end = lib_vrf_cli_write_end,
.get_next = lib_vrf_get_next,
.get_keys = lib_vrf_get_keys,
.lookup_entry = lib_vrf_lookup_entry,
+ .lookup_next = lib_vrf_lookup_next,
},
.priority = NB_DFLT_PRIORITY - 2,
},
@@ -1019,3 +1078,19 @@ const struct frr_yang_module_info frr_vrf_info = {
}
};
+const struct frr_yang_module_info frr_vrf_cli_info = {
+ .name = "frr-vrf",
+ .ignore_cfg_cbs = true,
+ .nodes = {
+ {
+ .xpath = "/frr-vrf:lib/vrf",
+ .cbs = {
+ .cli_show = lib_vrf_cli_write,
+ .cli_show_end = lib_vrf_cli_write_end,
+ },
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
diff --git a/lib/vrf.h b/lib/vrf.h
index 956730bac6..3ebb6ddf53 100644
--- a/lib/vrf.h
+++ b/lib/vrf.h
@@ -167,15 +167,14 @@ extern void *vrf_info_lookup(vrf_id_t);
/*
* VRF bit-map: maintaining flags, one bit per VRF ID
*/
-
typedef void *vrf_bitmap_t;
#define VRF_BITMAP_NULL NULL
-extern vrf_bitmap_t vrf_bitmap_init(void);
-extern void vrf_bitmap_free(vrf_bitmap_t);
-extern void vrf_bitmap_set(vrf_bitmap_t, vrf_id_t);
-extern void vrf_bitmap_unset(vrf_bitmap_t, vrf_id_t);
-extern int vrf_bitmap_check(vrf_bitmap_t, vrf_id_t);
+extern void vrf_bitmap_init(vrf_bitmap_t *pbmap);
+extern void vrf_bitmap_free(vrf_bitmap_t *pbmap);
+extern void vrf_bitmap_set(vrf_bitmap_t *pbmap, vrf_id_t vrf_id);
+extern void vrf_bitmap_unset(vrf_bitmap_t *pbmap, vrf_id_t vrf_id);
+extern int vrf_bitmap_check(vrf_bitmap_t *pbmap, vrf_id_t vrf_id);
/*
* VRF initializer/destructor
@@ -203,6 +202,12 @@ extern void vrf_init(int (*create)(struct vrf *vrf),
int (*destroy)(struct vrf *vrf));
/*
+ * Iterate over custom VRFs and round up by processing the default VRF.
+ */
+typedef void (*vrf_iter_func)(struct vrf *vrf);
+extern void vrf_iterate(vrf_iter_func fnc);
+
+/*
* Call vrf_terminate when the protocol is being shutdown
*/
extern void vrf_terminate(void);
@@ -295,6 +300,7 @@ extern int vrf_enable(struct vrf *vrf);
extern void vrf_delete(struct vrf *vrf);
extern const struct frr_yang_module_info frr_vrf_info;
+extern const struct frr_yang_module_info frr_vrf_cli_info;
#ifdef __cplusplus
}
diff --git a/lib/vty.c b/lib/vty.c
index e58e0293fb..912c893556 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -6,6 +6,8 @@
#include <zebra.h>
+#include <fcntl.h>
+#include <sys/stat.h>
#include <lib/version.h>
#include <sys/types.h>
#include <sys/types.h>
@@ -21,6 +23,7 @@
#endif /* HAVE_LIBPCRE2_POSIX */
#include <stdio.h>
+#include "debug.h"
#include "linklist.h"
#include "frrevent.h"
#include "buffer.h"
@@ -43,6 +46,8 @@
#include <arpa/telnet.h>
#include <termios.h>
+#include "lib/config_paths.h"
+
#include "lib/vty_clippy.c"
DEFINE_MTYPE_STATIC(LIB, VTY, "VTY");
@@ -67,19 +72,11 @@ enum vty_event {
struct nb_config *vty_mgmt_candidate_config;
-static uintptr_t mgmt_lib_hndl;
+static struct mgmt_fe_client *mgmt_fe_client;
static bool mgmt_fe_connected;
-static bool mgmt_candidate_ds_wr_locked;
static uint64_t mgmt_client_id_next;
static uint64_t mgmt_last_req_id = UINT64_MAX;
-static bool vty_debug;
-#define VTY_DBG(fmt, ...) \
- do { \
- if (vty_debug) \
- zlog_debug(fmt, ##__VA_ARGS__); \
- } while (0)
-
PREDECL_DLIST(vtyservs);
struct vty_serv {
@@ -125,37 +122,86 @@ static int no_password_check = 0;
/* Integrated configuration file path */
static char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG;
-static bool do_log_commands;
-static bool do_log_commands_perm;
+bool vty_log_commands;
+static bool vty_log_commands_perm;
+
+char const *const mgmt_daemons[] = {
+ "zebra",
+#ifdef HAVE_RIPD
+ "ripd",
+#endif
+#ifdef HAVE_RIPNGD
+ "ripngd",
+#endif
+#ifdef HAVE_STATICD
+ "staticd",
+#endif
+};
+uint mgmt_daemons_count = array_size(mgmt_daemons);
+
+
+static int vty_mgmt_lock_candidate_inline(struct vty *vty)
+{
+ assert(!vty->mgmt_locked_candidate_ds);
+ (void)vty_mgmt_send_lockds_req(vty, MGMTD_DS_CANDIDATE, true, true);
+ return vty->mgmt_locked_candidate_ds ? 0 : -1;
+}
+
+static int vty_mgmt_unlock_candidate_inline(struct vty *vty)
+{
+ assert(vty->mgmt_locked_candidate_ds);
+ (void)vty_mgmt_send_lockds_req(vty, MGMTD_DS_CANDIDATE, false, true);
+ return vty->mgmt_locked_candidate_ds ? -1 : 0;
+}
+
+static int vty_mgmt_lock_running_inline(struct vty *vty)
+{
+ assert(!vty->mgmt_locked_running_ds);
+ (void)vty_mgmt_send_lockds_req(vty, MGMTD_DS_RUNNING, true, true);
+ return vty->mgmt_locked_running_ds ? 0 : -1;
+}
-void vty_mgmt_resume_response(struct vty *vty, bool success)
+static int vty_mgmt_unlock_running_inline(struct vty *vty)
+{
+ assert(vty->mgmt_locked_running_ds);
+ (void)vty_mgmt_send_lockds_req(vty, MGMTD_DS_RUNNING, false, true);
+ return vty->mgmt_locked_running_ds ? -1 : 0;
+}
+
+void vty_mgmt_resume_response(struct vty *vty, int ret)
{
uint8_t header[4] = {0, 0, 0, 0};
- int ret = CMD_SUCCESS;
- if (!vty->mgmt_req_pending) {
+ if (!vty->mgmt_req_pending_cmd) {
zlog_err(
- "vty response called without setting mgmt_req_pending");
+ "vty resume response called without mgmt_req_pending_cmd");
return;
}
- if (!success)
- ret = CMD_WARNING_CONFIG_FAILED;
+ debug_fe_client("resuming CLI cmd after %s on vty session-id: %" PRIu64
+ " with '%s'",
+ vty->mgmt_req_pending_cmd, vty->mgmt_session_id,
+ ret == CMD_SUCCESS ? "success" : "failed");
- vty->mgmt_req_pending = false;
- header[3] = ret;
- buffer_put(vty->obuf, header, 4);
+ vty->mgmt_req_pending_cmd = NULL;
- if (!vty->t_write && (vtysh_flush(vty) < 0))
- /* Try to flush results; exit if a write
- * error occurs.
- */
- return;
+ if (vty->type != VTY_FILE) {
+ header[3] = ret;
+ buffer_put(vty->obuf, header, 4);
+ if (!vty->t_write && (vtysh_flush(vty) < 0)) {
+ zlog_err("failed to vtysh_flush");
+ /* Try to flush results; exit if a write error occurs */
+ return;
+ }
+ }
if (vty->status == VTY_CLOSE)
vty_close(vty);
- else
+ else if (vty->type != VTY_FILE)
vty_event(VTYSH_READ, vty);
+ else
+ /* should we assert here? */
+ zlog_err("mgmtd: unexpected resume while reading config file");
}
void vty_frame(struct vty *vty, const char *format, ...)
@@ -343,11 +389,14 @@ 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_empty(struct vty *vty)
+void vty_json_empty(struct vty *vty, struct json_object *json)
{
- json_object *json = json_object_new_object();
+ json_object *jsonobj = json;
+
+ if (!json)
+ jsonobj = json_object_new_object();
- vty_json(vty, json);
+ vty_json(vty, jsonobj);
}
/* Output current time to the vty. */
@@ -508,7 +557,7 @@ static int vty_command(struct vty *vty, char *buf)
/*
* Log non empty command lines
*/
- if (do_log_commands &&
+ if (vty_log_commands &&
strncmp(buf, "echo PING", strlen("echo PING")) != 0)
cp = buf;
if (cp != NULL) {
@@ -1525,7 +1574,7 @@ static void vty_read(struct event *thread)
break;
case '\r':
vty->escape = VTY_CR;
- /* fallthru */
+ fallthrough;
case '\n':
vty_out(vty, "\n");
buffer_flush_available(vty->obuf, vty->wfd);
@@ -1632,14 +1681,16 @@ struct vty *vty_new(void)
new->max = VTY_BUFSIZ;
new->pass_fd = -1;
- if (mgmt_lib_hndl) {
+ if (mgmt_fe_client) {
+ if (!mgmt_client_id_next)
+ mgmt_client_id_next++;
new->mgmt_client_id = mgmt_client_id_next++;
- if (mgmt_fe_create_client_session(
- mgmt_lib_hndl, new->mgmt_client_id,
- (uintptr_t) new) != MGMTD_SUCCESS)
- zlog_err(
- "Failed to open a MGMTD Frontend session for VTY session %p!!",
- new);
+ new->mgmt_session_id = 0;
+ mgmt_fe_create_client_session(
+ mgmt_fe_client, new->mgmt_client_id, (uintptr_t) new);
+ /* we short-circuit create the session so it must be set now */
+ assertf(new->mgmt_session_id != 0,
+ "Failed to create client session for VTY");
}
return new;
@@ -2173,6 +2224,77 @@ void vty_pass_fd(struct vty *vty, int fd)
vty->pass_fd = fd;
}
+bool mgmt_vty_read_configs(void)
+{
+ char path[PATH_MAX];
+ struct vty *vty;
+ FILE *confp;
+ uint line_num = 0;
+ uint count = 0;
+ uint index;
+
+ vty = vty_new();
+ vty->wfd = STDERR_FILENO;
+ vty->type = VTY_FILE;
+ vty->node = CONFIG_NODE;
+ vty->config = true;
+ vty->pending_allowed = true;
+
+ vty->candidate_config = vty_shared_candidate_config;
+
+ vty_mgmt_lock_candidate_inline(vty);
+ vty_mgmt_lock_running_inline(vty);
+
+ for (index = 0; index < array_size(mgmt_daemons); index++) {
+ snprintf(path, sizeof(path), "%s/%s.conf", frr_sysconfdir,
+ mgmt_daemons[index]);
+
+ confp = vty_open_config(path, config_default);
+ if (!confp)
+ continue;
+
+ zlog_info("mgmtd: reading config file: %s", path);
+
+ /* Execute configuration file */
+ line_num = 0;
+ (void)config_from_file(vty, confp, &line_num);
+ count++;
+
+ fclose(confp);
+ }
+
+ snprintf(path, sizeof(path), "%s/mgmtd.conf", frr_sysconfdir);
+ confp = vty_open_config(path, config_default);
+ if (confp) {
+ zlog_info("mgmtd: reading config file: %s", path);
+
+ line_num = 0;
+ (void)config_from_file(vty, confp, &line_num);
+ count++;
+
+ fclose(confp);
+ }
+
+ /* Conditionally unlock as the config file may have "exit"d early which
+ * would then have unlocked things.
+ */
+ if (vty->mgmt_locked_running_ds)
+ vty_mgmt_unlock_running_inline(vty);
+ if (vty->mgmt_locked_candidate_ds)
+ vty_mgmt_unlock_candidate_inline(vty);
+
+ vty->pending_allowed = false;
+
+ if (!count)
+ vty_close(vty);
+ else
+ vty_read_file_finish(vty, NULL);
+
+ zlog_info("mgmtd: finished reading config files");
+
+ return true;
+}
+
static void vtysh_read(struct event *thread)
{
int ret;
@@ -2186,6 +2308,19 @@ static void vtysh_read(struct event *thread)
sock = EVENT_FD(thread);
vty = EVENT_ARG(thread);
+ /*
+ * This code looks like it can read multiple commands from the `buf`
+ * value returned by read(); however, it cannot in some cases.
+ *
+ * There are multiple paths out of the "copying to vty->buf" loop, which
+ * lose any content not yet copied from the stack `buf`, `passfd`,
+ * `CMD_SUSPEND` and finally if a front-end for mgmtd (generally this
+ * would be mgmtd itself). So these code paths are counting on vtysh not
+ * sending us more than 1 command line before waiting on the reply to
+ * that command.
+ */
+ assert(vty->type == VTY_SHELL_SERV);
+
if ((nbytes = read(sock, buf, VTY_READ_BUFSIZ)) <= 0) {
if (nbytes < 0) {
if (ERRNO_IO_RETRY(errno)) {
@@ -2229,8 +2364,7 @@ static void vtysh_read(struct event *thread)
printf("result: %d\n", ret);
printf("vtysh node: %d\n", vty->node);
#endif /* VTYSH_DEBUG */
-
- if (vty->pass_fd != -1) {
+ if (vty->pass_fd >= 0) {
memset(vty->pass_fd_status, 0, 4);
vty->pass_fd_status[3] = ret;
vty->status = VTY_PASSFD;
@@ -2248,6 +2382,13 @@ static void vtysh_read(struct event *thread)
* => skip vty_event(VTYSH_READ, vty)!
*/
return;
+ } else {
+ assertf(vty->status != VTY_PASSFD,
+ "%p address=%s passfd=%d", vty,
+ vty->address, vty->pass_fd);
+
+ /* normalize other invalid values */
+ vty->pass_fd = -1;
}
/* hack for asynchronous "write integrated"
@@ -2260,8 +2401,12 @@ static void vtysh_read(struct event *thread)
/* with new infra we need to stop response till
* we get response through callback.
*/
- if (vty->mgmt_req_pending)
+ if (vty->mgmt_req_pending_cmd) {
+ debug_fe_client("postpone CLI response pending mgmtd %s on vty session-id %" PRIu64,
+ vty->mgmt_req_pending_cmd,
+ vty->mgmt_session_id);
return;
+ }
/* warning: watchfrr hardcodes this result write
*/
@@ -2292,7 +2437,7 @@ static void vtysh_write(struct event *thread)
#endif /* VTYSH */
/* Determine address family to bind. */
-void vty_serv_sock(const char *addr, unsigned short port, const char *path)
+void vty_serv_start(const char *addr, unsigned short port, const char *path)
{
/* If port is set to 0, do not listen on TCP/IP at all! */
if (port)
@@ -2303,6 +2448,20 @@ void vty_serv_sock(const char *addr, unsigned short port, const char *path)
#endif /* VTYSH */
}
+void vty_serv_stop(void)
+{
+ struct vty_serv *vtyserv;
+
+ while ((vtyserv = vtyservs_pop(vty_servs))) {
+ EVENT_OFF(vtyserv->t_accept);
+ close(vtyserv->sock);
+ XFREE(MTYPE_VTY_SERV, vtyserv);
+ }
+
+ vtyservs_fini(vty_servs);
+ vtyservs_init(vty_servs);
+}
+
static void vty_error_delete(void *arg)
{
struct vty_error *ve = arg;
@@ -2319,15 +2478,26 @@ void vty_close(struct vty *vty)
int i;
bool was_stdio = false;
- if (mgmt_lib_hndl) {
- mgmt_fe_destroy_client_session(mgmt_lib_hndl,
- vty->mgmt_client_id);
- vty->mgmt_session_id = 0;
- }
+ vty->status = VTY_CLOSE;
+
+ /*
+ * If we reach here with pending config to commit we will be losing it
+ * so warn the user.
+ */
+ if (vty->mgmt_num_pending_setcfg)
+ log_err_fe_client(
+ "vty closed, uncommitted config will be lost.");
/* Drop out of configure / transaction if needed. */
vty_config_exit(vty);
+ if (mgmt_fe_client && vty->mgmt_session_id) {
+ debug_fe_client("closing vty session");
+ mgmt_fe_destroy_client_session(mgmt_fe_client,
+ vty->mgmt_client_id);
+ vty->mgmt_session_id = 0;
+ }
+
/* Cancel threads.*/
EVENT_OFF(vty->t_read);
EVENT_OFF(vty->t_write);
@@ -2355,7 +2525,7 @@ void vty_close(struct vty *vty)
if (vty->fd != -1) {
if (vty->type == VTY_SHELL_SERV)
vtys_del(vtysh_sessions, vty);
- else
+ else if (vty->type == VTY_TERM)
vtys_del(vty_sessions, vty);
}
@@ -2374,6 +2544,7 @@ void vty_close(struct vty *vty)
if (vty->fd == STDIN_FILENO)
was_stdio = true;
+ XFREE(MTYPE_TMP, vty->pending_cmds_buf);
XFREE(MTYPE_VTY, vty->buf);
if (vty->error) {
@@ -2409,10 +2580,7 @@ static void vty_timeout(struct event *thread)
/* Read up configuration file from file_name. */
void vty_read_file(struct nb_config *config, FILE *confp)
{
- int ret;
struct vty *vty;
- struct vty_error *ve;
- struct listnode *node;
unsigned int line_num = 0;
vty = vty_new();
@@ -2435,16 +2603,30 @@ void vty_read_file(struct nb_config *config, FILE *confp)
}
/* Execute configuration file */
- ret = config_from_file(vty, confp, &line_num);
+ (void)config_from_file(vty, confp, &line_num);
+
+ vty_read_file_finish(vty, config);
+}
+
+void vty_read_file_finish(struct vty *vty, struct nb_config *config)
+{
+ struct vty_error *ve;
+ struct listnode *node;
/* Flush any previous errors before printing messages below */
buffer_flush_all(vty->obuf, vty->wfd);
- if (!((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO))) {
+ for (ALL_LIST_ELEMENTS_RO(vty->error, node, ve)) {
const char *message = NULL;
char *nl;
- switch (ret) {
+ switch (ve->cmd_ret) {
+ case CMD_SUCCESS:
+ message = "Command succeeded";
+ break;
+ case CMD_ERR_NOTHING_TODO:
+ message = "Nothing to do";
+ break;
case CMD_ERR_AMBIGUOUS:
message = "Ambiguous command";
break;
@@ -2469,13 +2651,11 @@ void vty_read_file(struct nb_config *config, FILE *confp)
break;
}
- for (ALL_LIST_ELEMENTS_RO(vty->error, node, ve)) {
- nl = strchr(ve->error_buf, '\n');
- if (nl)
- *nl = '\0';
- flog_err(EC_LIB_VTY, "%s on config line %u: %s",
- message, ve->line_num, ve->error_buf);
- }
+ nl = strchr(ve->error_buf, '\n');
+ if (nl)
+ *nl = '\0';
+ flog_err(EC_LIB_VTY, "%s on config line %u: %s", message,
+ ve->line_num, ve->error_buf);
}
/*
@@ -2485,6 +2665,7 @@ void vty_read_file(struct nb_config *config, FILE *confp)
if (config == NULL) {
struct nb_context context = {};
char errmsg[BUFSIZ] = {0};
+ int ret;
context.client = NB_CLIENT_CLI;
context.user = vty;
@@ -2555,15 +2736,12 @@ static FILE *vty_use_backup_config(const char *fullpath)
return ret;
}
-/* Read up configuration file from file_name. */
-bool vty_read_config(struct nb_config *config, const char *config_file,
- char *config_default_dir)
+FILE *vty_open_config(const char *config_file, char *config_default_dir)
{
char cwd[MAXPATHLEN];
FILE *confp = NULL;
const char *fullpath;
char *tmp = NULL;
- bool read_success = false;
/* If -f flag specified. */
if (config_file != NULL) {
@@ -2626,10 +2804,8 @@ bool vty_read_config(struct nb_config *config, const char *config_file,
if (strstr(config_default_dir, "vtysh") == NULL) {
ret = stat(integrate_default, &conf_stat);
- if (ret >= 0) {
- read_success = true;
+ if (ret >= 0)
goto tmp_free_and_out;
- }
}
#endif /* VTYSH */
confp = fopen(config_default_dir, "r");
@@ -2655,66 +2831,66 @@ bool vty_read_config(struct nb_config *config, const char *config_file,
fullpath = config_default_dir;
}
- vty_read_file(config, confp);
- read_success = true;
-
- fclose(confp);
-
host_config_set(fullpath);
tmp_free_and_out:
XFREE(MTYPE_TMP, tmp);
- return read_success;
+ return confp;
}
-static void update_xpath(struct vty *vty, const char *oldpath,
- const char *newpath)
+
+bool vty_read_config(struct nb_config *config, const char *config_file,
+ char *config_default_dir)
{
- int i;
+ FILE *confp;
- for (i = 0; i < vty->xpath_index; i++) {
- if (!frrstr_startswith(vty->xpath[i], oldpath))
- break;
+ confp = vty_open_config(config_file, config_default_dir);
+ if (!confp)
+ return false;
- char *tmp = frrstr_replace(vty->xpath[i], oldpath, newpath);
- strlcpy(vty->xpath[i], tmp, sizeof(vty->xpath[0]));
- XFREE(MTYPE_TMP, tmp);
- }
-}
+ vty_read_file(config, confp);
-void vty_update_xpath(const char *oldpath, const char *newpath)
-{
- struct vty *vty;
+ fclose(confp);
- frr_each (vtys, vtysh_sessions, vty)
- update_xpath(vty, oldpath, newpath);
- frr_each (vtys, vty_sessions, vty)
- update_xpath(vty, oldpath, newpath);
+ return true;
}
-int vty_config_enter(struct vty *vty, bool private_config, bool exclusive)
+int vty_config_enter(struct vty *vty, bool private_config, bool exclusive,
+ bool file_lock)
{
- if (exclusive && nb_running_lock(NB_CLIENT_CLI, vty)) {
+ if (exclusive && !vty_mgmt_fe_enabled() &&
+ nb_running_lock(NB_CLIENT_CLI, vty)) {
vty_out(vty, "%% Configuration is locked by other client\n");
return CMD_WARNING;
}
- if (vty_mgmt_fe_enabled()) {
- if (!mgmt_candidate_ds_wr_locked) {
- if (vty_mgmt_send_lockds_req(vty, MGMTD_DS_CANDIDATE,
- true) != 0) {
- vty_out(vty, "Not able to lock candidate DS\n");
- return CMD_WARNING;
- }
- } else {
+ /*
+ * We only need to do a lock when reading a config file as we will be
+ * sending a batch of setcfg changes followed by a single commit
+ * message. For user interactive mode we are doing implicit commits
+ * those will obtain the lock (or not) when they try and commit.
+ */
+ if (file_lock && vty_mgmt_fe_enabled() && !private_config) {
+ if (vty_mgmt_lock_candidate_inline(vty)) {
vty_out(vty,
- "Candidate DS already locked by different session\n");
- return CMD_WARNING;
+ "%% Can't enter config; candidate datastore locked by another session\n");
+ return CMD_WARNING_CONFIG_FAILED;
}
+ if (vty_mgmt_lock_running_inline(vty)) {
+ vty_out(vty,
+ "%% Can't enter config; running datastore locked by another session\n");
+ vty_mgmt_unlock_candidate_inline(vty);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ assert(vty->mgmt_locked_candidate_ds);
+ assert(vty->mgmt_locked_running_ds);
- vty->mgmt_locked_candidate_ds = true;
- mgmt_candidate_ds_wr_locked = true;
+ /*
+ * As datastores are locked explicitly, we don't need implicit
+ * commits and should allow pending changes.
+ */
+ vty->pending_allowed = true;
}
vty->node = CONFIG_NODE;
@@ -2727,23 +2903,24 @@ int vty_config_enter(struct vty *vty, bool private_config, bool exclusive)
vty->candidate_config_base = nb_config_dup(running_config);
vty_out(vty,
"Warning: uncommitted changes will be discarded on exit.\n\n");
- } else {
- /*
- * NOTE: On the MGMTD daemon we point the VTY candidate DS to
- * the global MGMTD candidate DS. Else we point to the VTY
- * Shared Candidate Config.
- */
- vty->candidate_config = vty_mgmt_candidate_config
- ? vty_mgmt_candidate_config
- : vty_shared_candidate_config;
- if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL)
- vty->candidate_config_base =
- nb_config_dup(running_config);
+ return CMD_SUCCESS;
}
+ /*
+ * NOTE: On the MGMTD daemon we point the VTY candidate DS to
+ * the global MGMTD candidate DS. Else we point to the VTY
+ * Shared Candidate Config.
+ */
+ vty->candidate_config = vty_mgmt_candidate_config
+ ? vty_mgmt_candidate_config
+ : vty_shared_candidate_config;
+ if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL)
+ vty->candidate_config_base = nb_config_dup(running_config);
+
return CMD_SUCCESS;
}
+
void vty_config_exit(struct vty *vty)
{
enum node_type node = vty->node;
@@ -2768,17 +2945,15 @@ int vty_config_node_exit(struct vty *vty)
{
vty->xpath_index = 0;
- if (vty_mgmt_fe_enabled() && mgmt_candidate_ds_wr_locked &&
- vty->mgmt_locked_candidate_ds) {
- if (vty_mgmt_send_lockds_req(vty, MGMTD_DS_CANDIDATE, false) !=
- 0) {
- vty_out(vty, "Not able to unlock candidate DS\n");
- return CMD_WARNING;
- }
+ /* TODO: could we check for un-commited changes here? */
- vty->mgmt_locked_candidate_ds = false;
- mgmt_candidate_ds_wr_locked = false;
- }
+ vty->pending_allowed = false;
+
+ if (vty->mgmt_locked_running_ds)
+ vty_mgmt_unlock_running_inline(vty);
+
+ if (vty->mgmt_locked_candidate_ds)
+ vty_mgmt_unlock_candidate_inline(vty);
/* Perform any pending commits. */
(void)nb_cli_pending_commit_check(vty);
@@ -2804,6 +2979,16 @@ int vty_config_node_exit(struct vty *vty)
}
vty->config = false;
+
+ /*
+ * If this is a config file and we are dropping out of config end
+ * parsing.
+ */
+ if (vty->type == VTY_FILE && vty->status != VTY_CLOSE) {
+ vty_out(vty, "exit from config node while reading config file");
+ vty->status = VTY_CLOSE;
+ }
+
return 1;
}
@@ -3160,15 +3345,15 @@ DEFPY (log_commands,
"Log all commands\n")
{
if (no) {
- if (do_log_commands_perm) {
+ if (vty_log_commands_perm) {
vty_out(vty,
"Daemon started with permanent logging turned on for commands, ignoring\n");
return CMD_WARNING;
}
- do_log_commands = false;
+ vty_log_commands = false;
} else
- do_log_commands = true;
+ vty_log_commands = true;
return CMD_SUCCESS;
}
@@ -3196,7 +3381,7 @@ static int vty_config_write(struct vty *vty)
vty_endframe(vty, "exit\n");
- if (do_log_commands)
+ if (vty_log_commands)
vty_out(vty, "log commands\n");
vty_out(vty, "!\n");
@@ -3277,25 +3462,42 @@ void vty_init_vtysh(void)
/* currently nothing to do, but likely to have future use */
}
-static void vty_mgmt_server_connected(uintptr_t lib_hndl, uintptr_t usr_data,
- bool connected)
-{
- VTY_DBG("%sGot %sconnected %s MGMTD Frontend Server",
- !connected ? "ERROR: " : "", !connected ? "dis: " : "",
- !connected ? "from" : "to");
- mgmt_fe_connected = connected;
+/*
+ * These functions allow for CLI handling to be placed inside daemons; however,
+ * currently they are only used by mgmtd, with mgmtd having each daemons CLI
+ * functionality linked into it. This design choice was taken for efficiency.
+ */
+
+static void vty_mgmt_server_connected(struct mgmt_fe_client *client,
+ uintptr_t usr_data, bool connected)
+{
+ debug_fe_client("Got %sconnected %s MGMTD Frontend Server",
+ !connected ? "dis: " : "", !connected ? "from" : "to");
/*
- * TODO: Setup or teardown front-end sessions for existing
- * VTY connections.
+ * We should not have any sessions for connecting or disconnecting case.
+ * The fe client library will delete all session on disconnect before
+ * calling us.
*/
+ assert(mgmt_fe_client_session_count(client) == 0);
+
+ mgmt_fe_connected = connected;
+
+ /* Start or stop listening for vty connections */
+ if (connected)
+ frr_vty_serv_start();
+ else
+ frr_vty_serv_stop();
}
-static void vty_mgmt_session_created(uintptr_t lib_hndl, uintptr_t usr_data,
- uint64_t client_id, bool create,
- bool success, uintptr_t session_id,
- uintptr_t session_ctx)
+/*
+ * A session has successfully been created for a vty.
+ */
+static void vty_mgmt_session_notify(struct mgmt_fe_client *client,
+ uintptr_t usr_data, uint64_t client_id,
+ bool create, bool success,
+ uintptr_t session_id, uintptr_t session_ctx)
{
struct vty *vty;
@@ -3307,40 +3509,58 @@ static void vty_mgmt_session_created(uintptr_t lib_hndl, uintptr_t usr_data,
return;
}
- VTY_DBG("%s session for client %" PRIu64 " successfully",
- create ? "Created" : "Destroyed", client_id);
- if (create)
+ debug_fe_client("%s session for client %" PRIu64 " successfully",
+ create ? "Created" : "Destroyed", client_id);
+
+ if (create) {
+ assert(session_id != 0);
vty->mgmt_session_id = session_id;
+ } else {
+ vty->mgmt_session_id = 0;
+ /* We may come here by way of vty_close() and short-circuits */
+ if (vty->status != VTY_CLOSE)
+ vty_close(vty);
+ }
}
-static void vty_mgmt_ds_lock_notified(uintptr_t lib_hndl, uintptr_t usr_data,
- uint64_t client_id, uintptr_t session_id,
+static void vty_mgmt_ds_lock_notified(struct mgmt_fe_client *client,
+ uintptr_t usr_data, uint64_t client_id,
+ uintptr_t session_id,
uintptr_t session_ctx, uint64_t req_id,
bool lock_ds, bool success,
Mgmtd__DatastoreId ds_id,
char *errmsg_if_any)
{
struct vty *vty;
+ bool is_short_circuit = mgmt_fe_client_current_msg_short_circuit(client);
vty = (struct vty *)session_ctx;
- if (!success) {
- zlog_err("%socking for DS %u failed, Err: '%s'",
- lock_ds ? "L" : "Unl", ds_id, errmsg_if_any);
- vty_out(vty, "ERROR: %socking for DS %u failed, Err: '%s'\n",
- lock_ds ? "L" : "Unl", ds_id, errmsg_if_any);
- } else {
- VTY_DBG("%socked DS %u successfully", lock_ds ? "L" : "Unl",
- ds_id);
+ assert(ds_id == MGMTD_DS_CANDIDATE || ds_id == MGMTD_DS_RUNNING);
+ if (!success)
+ zlog_err("%socking for DS %u failed, Err: '%s' vty %p",
+ lock_ds ? "L" : "Unl", ds_id, errmsg_if_any, vty);
+ else {
+ debug_fe_client("%socked DS %u successfully",
+ lock_ds ? "L" : "Unl", ds_id);
+ if (ds_id == MGMTD_DS_CANDIDATE)
+ vty->mgmt_locked_candidate_ds = lock_ds;
+ else
+ vty->mgmt_locked_running_ds = lock_ds;
}
- vty_mgmt_resume_response(vty, success);
+ if (!is_short_circuit && vty->mgmt_req_pending_cmd) {
+ assert(!strcmp(vty->mgmt_req_pending_cmd, "MESSAGE_LOCKDS_REQ"));
+ vty_mgmt_resume_response(vty,
+ success ? CMD_SUCCESS : CMD_WARNING);
+ }
}
static void vty_mgmt_set_config_result_notified(
- uintptr_t lib_hndl, uintptr_t usr_data, uint64_t client_id,
+ struct mgmt_fe_client *client, uintptr_t usr_data, uint64_t client_id,
uintptr_t session_id, uintptr_t session_ctx, uint64_t req_id,
- bool success, Mgmtd__DatastoreId ds_id, char *errmsg_if_any)
+ bool success, Mgmtd__DatastoreId ds_id, bool implicit_commit,
+ char *errmsg_if_any)
{
struct vty *vty;
@@ -3350,19 +3570,27 @@ 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 {
- VTY_DBG("SET_CONFIG request for client 0x%" PRIx64
- " req-id %" PRIu64 " was successfull",
- client_id, req_id);
+ debug_fe_client("SET_CONFIG request for client 0x%" PRIx64
+ " req-id %" PRIu64 " was successfull",
+ client_id, req_id);
+ }
+
+ if (implicit_commit) {
+ /* In this case the changes have been applied, we are done */
+ vty_mgmt_unlock_candidate_inline(vty);
+ vty_mgmt_unlock_running_inline(vty);
}
- vty_mgmt_resume_response(vty, success);
+ vty_mgmt_resume_response(vty, success ? CMD_SUCCESS
+ : CMD_WARNING_CONFIG_FAILED);
}
static void vty_mgmt_commit_config_result_notified(
- uintptr_t lib_hndl, uintptr_t usr_data, uint64_t client_id,
+ struct mgmt_fe_client *client, uintptr_t usr_data, uint64_t client_id,
uintptr_t session_id, uintptr_t session_ctx, uint64_t req_id,
bool success, Mgmtd__DatastoreId src_ds_id,
Mgmtd__DatastoreId dst_ds_id, bool validate_only, char *errmsg_if_any)
@@ -3375,21 +3603,23 @@ 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 {
- VTY_DBG("COMMIT_CONFIG request for client 0x%" PRIx64
- " req-id %" PRIu64 " was successfull",
- client_id, req_id);
+ debug_fe_client("COMMIT_CONFIG request for client 0x%" PRIx64
+ " req-id %" PRIu64 " was successfull",
+ client_id, req_id);
if (errmsg_if_any)
vty_out(vty, "MGMTD: %s\n", errmsg_if_any);
}
- vty_mgmt_resume_response(vty, success);
+ vty_mgmt_resume_response(vty, success ? CMD_SUCCESS
+ : CMD_WARNING_CONFIG_FAILED);
}
-static enum mgmt_result vty_mgmt_get_data_result_notified(
- uintptr_t lib_hndl, uintptr_t usr_data, uint64_t client_id,
+static int vty_mgmt_get_data_result_notified(
+ struct mgmt_fe_client *client, uintptr_t usr_data, uint64_t client_id,
uintptr_t session_id, uintptr_t session_ctx, uint64_t req_id,
bool success, Mgmtd__DatastoreId ds_id, Mgmtd__YangData **yang_data,
size_t num_data, int next_key, char *errmsg_if_any)
@@ -3405,13 +3635,13 @@ static enum mgmt_result vty_mgmt_get_data_result_notified(
client_id, errmsg_if_any ? errmsg_if_any : "Unknown");
vty_out(vty, "ERROR: GET_DATA request failed, Error: %s\n",
errmsg_if_any ? errmsg_if_any : "Unknown");
- vty_mgmt_resume_response(vty, success);
- return MGMTD_INTERNAL_ERROR;
+ vty_mgmt_resume_response(vty, CMD_WARNING);
+ return -1;
}
- VTY_DBG("GET_DATA request for client 0x%" PRIx64 " req-id %" PRIu64
- " was successfull!",
- client_id, req_id);
+ debug_fe_client("GET_DATA request succeeded, client 0x%" PRIx64
+ " req-id %" PRIu64,
+ client_id, req_id);
if (req_id != mgmt_last_req_id) {
mgmt_last_req_id = req_id;
@@ -3424,171 +3654,420 @@ static enum mgmt_result vty_mgmt_get_data_result_notified(
}
if (next_key < 0) {
vty_out(vty, "]\n");
- vty_mgmt_resume_response(vty, success);
+ vty_mgmt_resume_response(vty, CMD_SUCCESS);
+ }
+
+ return 0;
+}
+
+static ssize_t vty_mgmt_libyang_print(void *user_data, const void *buf,
+ size_t count)
+{
+ struct vty *vty = user_data;
+
+ vty_out(vty, "%.*s", (int)count, (const char *)buf);
+ return count;
+}
+
+static void vty_out_yang_error(struct vty *vty, LYD_FORMAT format,
+ struct ly_err_item *ei)
+{
+ bool have_apptag = ei->apptag && ei->apptag[0] != 0;
+ bool have_path = ei->path && ei->path[0] != 0;
+ bool have_msg = ei->msg && ei->msg[0] != 0;
+ const char *severity = NULL;
+ const char *evalid = NULL;
+ const char *ecode = NULL;
+ LY_ERR err = ei->no;
+
+ if (ei->level == LY_LLERR)
+ severity = "error";
+ else if (ei->level == LY_LLWRN)
+ severity = "warning";
+
+ ecode = yang_ly_strerrcode(err);
+ if (err == LY_EVALID && ei->vecode != LYVE_SUCCESS)
+ evalid = yang_ly_strvecode(ei->vecode);
+
+ switch (format) {
+ case LYD_XML:
+ vty_out(vty,
+ "<rpc-error xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">");
+ vty_out(vty, "<error-type>application</error-type>");
+ if (severity)
+ vty_out(vty, "<error-severity>%s</error-severity>",
+ severity);
+ if (ecode)
+ vty_out(vty, "<error-code>%s</error-code>", ecode);
+ if (evalid)
+ vty_out(vty, "<error-validation>%s</error-validation>\n",
+ evalid);
+ if (have_path)
+ vty_out(vty, "<error-path>%s</error-path>\n", ei->path);
+ if (have_apptag)
+ vty_out(vty, "<error-app-tag>%s</error-app-tag>\n",
+ ei->apptag);
+ if (have_msg)
+ vty_out(vty, "<error-message>%s</error-message>\n",
+ ei->msg);
+
+ vty_out(vty, "</rpc-error>");
+ break;
+ case LYD_JSON:
+ vty_out(vty, "{ \"error-type\": \"application\"");
+ if (severity)
+ vty_out(vty, ", \"error-severity\": \"%s\"", severity);
+ if (ecode)
+ vty_out(vty, ", \"error-code\": \"%s\"", ecode);
+ if (evalid)
+ vty_out(vty, ", \"error-validation\": \"%s\"", evalid);
+ if (have_path)
+ vty_out(vty, ", \"error-path\": \"%s\"", ei->path);
+ if (have_apptag)
+ vty_out(vty, ", \"error-app-tag\": \"%s\"", ei->apptag);
+ if (have_msg)
+ vty_out(vty, ", \"error-message\": \"%s\"", ei->msg);
+
+ vty_out(vty, "}");
+ break;
+ case LYD_UNKNOWN:
+ case LYD_LYB:
+ default:
+ vty_out(vty, "%% error");
+ if (severity)
+ vty_out(vty, " severity: %s", severity);
+ if (evalid)
+ vty_out(vty, " invalid: %s", evalid);
+ if (have_path)
+ vty_out(vty, " path: %s", ei->path);
+ if (have_apptag)
+ vty_out(vty, " app-tag: %s", ei->apptag);
+ if (have_msg)
+ vty_out(vty, " msg: %s", ei->msg);
+ break;
+ }
+}
+
+static uint vty_out_yang_errors(struct vty *vty, LYD_FORMAT format)
+{
+ struct ly_err_item *ei = ly_err_first(ly_native_ctx);
+ uint count;
+
+ if (!ei)
+ return 0;
+
+ if (format == LYD_JSON)
+ vty_out(vty, "\"ietf-restconf:errors\": [ ");
+
+ for (count = 0; ei; count++, ei = ei->next) {
+ if (count)
+ vty_out(vty, ", ");
+ vty_out_yang_error(vty, format, ei);
+ }
+
+ if (format == LYD_JSON)
+ vty_out(vty, " ]");
+
+ ly_err_clean(ly_native_ctx, NULL);
+
+ return count;
+}
+
+
+static int vty_mgmt_get_tree_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,
+ Mgmtd__DatastoreId ds_id, LYD_FORMAT result_type, void *result,
+ size_t len, int partial_error)
+{
+ struct vty *vty;
+ struct lyd_node *dnode;
+ int ret = CMD_SUCCESS;
+ LY_ERR err;
+
+ vty = (struct vty *)session_ctx;
+
+ debug_fe_client("GET_TREE request %ssucceeded, client 0x%" PRIx64
+ " req-id %" PRIu64,
+ partial_error ? "partially " : "", client_id, req_id);
+
+ assert(result_type == LYD_LYB ||
+ result_type == vty->mgmt_req_pending_data);
+
+ if (vty->mgmt_req_pending_data == LYD_XML && partial_error)
+ vty_out(vty,
+ "<!-- some errors occurred gathering results -->\n");
+
+ if (result_type == LYD_LYB) {
+ /*
+ * parse binary into tree and print in the specified format
+ */
+ result_type = vty->mgmt_req_pending_data;
+
+ err = lyd_parse_data_mem(ly_native_ctx, result, LYD_LYB, 0, 0,
+ &dnode);
+ if (!err)
+ err = lyd_print_clb(vty_mgmt_libyang_print, vty, dnode,
+ result_type, LYD_PRINT_WITHSIBLINGS);
+ lyd_free_all(dnode);
+
+ if (vty_out_yang_errors(vty, result_type) || err)
+ ret = CMD_WARNING;
+ } else {
+ /*
+ * Print the in-format result
+ */
+ assert(result_type == LYD_XML || result_type == LYD_JSON);
+ vty_out(vty, "%.*s\n", (int)len - 1, (const char *)result);
}
- return MGMTD_SUCCESS;
+ vty_mgmt_resume_response(vty, ret);
+
+ return 0;
}
-static struct mgmt_fe_client_params client_params = {
+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,
+ uint64_t req_id, int error,
+ const char *errstr)
+{
+ struct vty *vty = (struct vty *)session_ctx;
+ const char *cname = mgmt_fe_client_name(client);
+
+ if (!vty->mgmt_req_pending_cmd) {
+ debug_fe_client("Erorr with no pending command: %d returned for client %s 0x%" PRIx64
+ " session-id %" PRIu64 " req-id %" PRIu64
+ "error-str %s",
+ error, cname, client_id, session_id, req_id,
+ errstr);
+ vty_out(vty,
+ "%% Error %d from MGMTD for %s with no pending command: %s\n",
+ error, cname, errstr);
+ return CMD_WARNING;
+ }
+
+ debug_fe_client("Erorr %d returned for client %s 0x%" PRIx64
+ " session-id %" PRIu64 " req-id %" PRIu64 "error-str %s",
+ error, cname, client_id, session_id, req_id, errstr);
+
+ vty_out(vty, "%% %s (for %s, client %s)\n", errstr,
+ vty->mgmt_req_pending_cmd, cname);
+
+ vty_mgmt_resume_response(vty, error ? CMD_WARNING : CMD_SUCCESS);
+
+ return 0;
+}
+
+static struct mgmt_fe_client_cbs mgmt_cbs = {
.client_connect_notify = vty_mgmt_server_connected,
- .client_session_notify = vty_mgmt_session_created,
+ .client_session_notify = vty_mgmt_session_notify,
.lock_ds_notify = vty_mgmt_ds_lock_notified,
.set_config_notify = vty_mgmt_set_config_result_notified,
.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,
+ .error_notify = vty_mgmt_error_notified,
+
};
void vty_init_mgmt_fe(void)
{
- if (!vty_master) {
- zlog_err("Always call vty_mgmt_init_fe() after vty_init()!!");
- return;
- }
-
- assert(!mgmt_lib_hndl);
- snprintf(client_params.name, sizeof(client_params.name), "%s-%lld",
- frr_get_progname(), (long long)getpid());
- mgmt_lib_hndl = mgmt_fe_client_lib_init(&client_params, vty_master);
- assert(mgmt_lib_hndl);
+ char name[40];
+
+ assert(vty_master);
+ assert(!mgmt_fe_client);
+ snprintf(name, sizeof(name), "vty-%s-%ld", frr_get_progname(),
+ (long)getpid());
+ mgmt_fe_client = mgmt_fe_client_create(name, &mgmt_cbs, 0, vty_master);
+ assert(mgmt_fe_client);
}
bool vty_mgmt_fe_enabled(void)
{
- return mgmt_lib_hndl && mgmt_fe_connected ? true : false;
+ return mgmt_fe_client && mgmt_fe_connected;
}
-int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id,
- bool lock)
+bool vty_mgmt_should_process_cli_apply_changes(struct vty *vty)
{
- enum mgmt_result ret;
+ return vty->type != VTY_FILE && vty_mgmt_fe_enabled();
+}
- if (mgmt_lib_hndl && vty->mgmt_session_id) {
- vty->mgmt_req_id++;
- ret = mgmt_fe_lock_ds(mgmt_lib_hndl, vty->mgmt_session_id,
- vty->mgmt_req_id, ds_id, lock);
- if (ret != MGMTD_SUCCESS) {
- zlog_err(
- "Failed to send %sLOCK-DS-REQ to MGMTD for req-id %"
- PRIu64 ".",
- lock ? "" : "UN", vty->mgmt_req_id);
- vty_out(vty, "Failed to send %sLOCK-DS-REQ to MGMTD!\n",
- lock ? "" : "UN");
- return -1;
- }
+int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id,
+ bool lock, bool scok)
+{
+ assert(mgmt_fe_client);
+ assert(vty->mgmt_session_id);
- vty->mgmt_req_pending = true;
+ vty->mgmt_req_id++;
+ if (mgmt_fe_send_lockds_req(mgmt_fe_client, vty->mgmt_session_id,
+ vty->mgmt_req_id, ds_id, lock, scok)) {
+ zlog_err("Failed sending %sLOCK-DS-REQ req-id %" PRIu64,
+ lock ? "" : "UN", vty->mgmt_req_id);
+ vty_out(vty, "Failed to send %sLOCK-DS-REQ to MGMTD!\n",
+ lock ? "" : "UN");
+ return -1;
}
+ if (!scok)
+ vty->mgmt_req_pending_cmd = "MESSAGE_LOCKDS_REQ";
+
return 0;
}
-int vty_mgmt_send_config_data(struct vty *vty)
+int vty_mgmt_send_config_data(struct vty *vty, const char *xpath_base,
+ bool implicit_commit)
{
Mgmtd__YangDataValue value[VTY_MAXCFGCHANGES];
Mgmtd__YangData cfg_data[VTY_MAXCFGCHANGES];
Mgmtd__YangCfgDataReq cfg_req[VTY_MAXCFGCHANGES];
Mgmtd__YangCfgDataReq *cfgreq[VTY_MAXCFGCHANGES] = {0};
+ char xpath[VTY_MAXCFGCHANGES][XPATH_MAXLEN];
+ char *change_xpath;
size_t indx;
- int cnt;
- bool implicit_commit = false;
-
- if (mgmt_lib_hndl && vty->mgmt_session_id) {
- cnt = 0;
- for (indx = 0; indx < vty->num_cfg_changes; indx++) {
- mgmt_yang_data_init(&cfg_data[cnt]);
-
- if (vty->cfg_changes[indx].value) {
- mgmt_yang_data_value_init(&value[cnt]);
- value[cnt].encoded_str_val =
- (char *)vty->cfg_changes[indx].value;
- value[cnt].value_case =
- MGMTD__YANG_DATA_VALUE__VALUE_ENCODED_STR_VAL;
- cfg_data[cnt].value = &value[cnt];
- }
- cfg_data[cnt].xpath = vty->cfg_changes[indx].xpath;
+ if (vty->type == VTY_FILE) {
+ /*
+ * if this is a config file read we will not send any of the
+ * changes until we are done reading the file and have modified
+ * the local candidate DS.
+ */
+ /* no-one else should be sending data right now */
+ assert(!vty->mgmt_num_pending_setcfg);
+ return 0;
+ }
- mgmt_yang_cfg_data_req_init(&cfg_req[cnt]);
- cfg_req[cnt].data = &cfg_data[cnt];
- switch (vty->cfg_changes[indx].operation) {
- case NB_OP_DESTROY:
- cfg_req[cnt].req_type =
- MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA;
- break;
+ /* If we are FE client and we have a vty then we have a session */
+ assert(mgmt_fe_client && vty->mgmt_client_id && vty->mgmt_session_id);
- case NB_OP_CREATE:
- case NB_OP_MODIFY:
- case NB_OP_MOVE:
- case NB_OP_PRE_VALIDATE:
- case NB_OP_APPLY_FINISH:
- cfg_req[cnt].req_type =
- MGMTD__CFG_DATA_REQ_TYPE__SET_DATA;
- break;
- case NB_OP_GET_ELEM:
- case NB_OP_GET_NEXT:
- case NB_OP_GET_KEYS:
- case NB_OP_LOOKUP_ENTRY:
- case NB_OP_RPC:
- assert(!"Invalid type of operation");
- break;
- default:
- assert(!"non-enum value, invalid");
- }
+ if (!vty->num_cfg_changes)
+ return 0;
- cfgreq[cnt] = &cfg_req[cnt];
- cnt++;
+ /* grab the candidate and running lock prior to sending implicit commit
+ * command
+ */
+ if (implicit_commit) {
+ if (vty_mgmt_lock_candidate_inline(vty)) {
+ vty_out(vty,
+ "%% command failed, could not lock candidate DS\n");
+ return -1;
+ } else if (vty_mgmt_lock_running_inline(vty)) {
+ vty_out(vty,
+ "%% command failed, could not lock running DS\n");
+ vty_mgmt_unlock_candidate_inline(vty);
+ return -1;
}
+ }
- vty->mgmt_req_id++;
- implicit_commit = vty_needs_implicit_commit(vty);
- if (cnt && mgmt_fe_set_config_data(
- mgmt_lib_hndl, vty->mgmt_session_id,
- vty->mgmt_req_id, MGMTD_DS_CANDIDATE, cfgreq,
- cnt, implicit_commit,
- MGMTD_DS_RUNNING) != MGMTD_SUCCESS) {
- zlog_err("Failed to send %d Config Xpaths to MGMTD!!",
- (int)indx);
- vty_out(vty, "Failed to send SETCFG-REQ to MGMTD!\n");
- return -1;
+ if (xpath_base == NULL)
+ xpath_base = "";
+
+ for (indx = 0; indx < vty->num_cfg_changes; indx++) {
+ mgmt_yang_data_init(&cfg_data[indx]);
+
+ if (vty->cfg_changes[indx].value) {
+ mgmt_yang_data_value_init(&value[indx]);
+ value[indx].encoded_str_val =
+ (char *)vty->cfg_changes[indx].value;
+ value[indx].value_case =
+ MGMTD__YANG_DATA_VALUE__VALUE_ENCODED_STR_VAL;
+ cfg_data[indx].value = &value[indx];
+ }
+
+ change_xpath = vty->cfg_changes[indx].xpath;
+
+ memset(xpath[indx], 0, sizeof(xpath[indx]));
+ /* If change xpath is relative, prepend base xpath. */
+ if (change_xpath[0] == '.') {
+ strlcpy(xpath[indx], xpath_base, sizeof(xpath[indx]));
+ change_xpath++; /* skip '.' */
+ }
+ strlcat(xpath[indx], change_xpath, sizeof(xpath[indx]));
+
+ cfg_data[indx].xpath = xpath[indx];
+
+ mgmt_yang_cfg_data_req_init(&cfg_req[indx]);
+ cfg_req[indx].data = &cfg_data[indx];
+ switch (vty->cfg_changes[indx].operation) {
+ case NB_OP_DELETE:
+ cfg_req[indx].req_type =
+ MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA;
+ break;
+
+ case NB_OP_DESTROY:
+ cfg_req[indx].req_type =
+ MGMTD__CFG_DATA_REQ_TYPE__REMOVE_DATA;
+ break;
+
+ case NB_OP_CREATE_EXCL:
+ cfg_req[indx].req_type =
+ MGMTD__CFG_DATA_REQ_TYPE__CREATE_DATA;
+ break;
+
+ case NB_OP_REPLACE:
+ cfg_req[indx].req_type =
+ MGMTD__CFG_DATA_REQ_TYPE__REPLACE_DATA;
+ break;
+
+ case NB_OP_CREATE:
+ case NB_OP_MODIFY:
+ case NB_OP_MOVE:
+ cfg_req[indx].req_type =
+ MGMTD__CFG_DATA_REQ_TYPE__SET_DATA;
+ break;
+ default:
+ assertf(false,
+ "Invalid operation type for send config: %d",
+ vty->cfg_changes[indx].operation);
+ /*NOTREACHED*/
+ abort();
}
- vty->mgmt_req_pending = true;
+ cfgreq[indx] = &cfg_req[indx];
}
+ if (!indx)
+ return 0;
+
+ vty->mgmt_req_id++;
+ if (mgmt_fe_send_setcfg_req(mgmt_fe_client, vty->mgmt_session_id,
+ vty->mgmt_req_id, MGMTD_DS_CANDIDATE,
+ cfgreq, indx, implicit_commit,
+ MGMTD_DS_RUNNING)) {
+ zlog_err("Failed to send %zu config xpaths to mgmtd", indx);
+ vty_out(vty, "%% Failed to send commands to mgmtd\n");
+ return -1;
+ }
+
+ vty->mgmt_req_pending_cmd = "MESSAGE_SETCFG_REQ";
return 0;
}
int vty_mgmt_send_commit_config(struct vty *vty, bool validate_only, bool abort)
{
- enum mgmt_result ret;
-
- if (mgmt_lib_hndl && vty->mgmt_session_id) {
+ if (mgmt_fe_client && vty->mgmt_session_id) {
vty->mgmt_req_id++;
- ret = mgmt_fe_commit_config_data(
- mgmt_lib_hndl, vty->mgmt_session_id, vty->mgmt_req_id,
- MGMTD_DS_CANDIDATE, MGMTD_DS_RUNNING, validate_only,
- abort);
- if (ret != MGMTD_SUCCESS) {
- zlog_err(
- "Failed to send COMMIT-REQ to MGMTD for req-id %"
- PRIu64 ".",
- vty->mgmt_req_id);
+ if (mgmt_fe_send_commitcfg_req(
+ mgmt_fe_client, vty->mgmt_session_id,
+ vty->mgmt_req_id, MGMTD_DS_CANDIDATE,
+ MGMTD_DS_RUNNING, validate_only, abort)) {
+ zlog_err("Failed sending COMMIT-REQ req-id %" PRIu64,
+ vty->mgmt_req_id);
vty_out(vty, "Failed to send COMMIT-REQ to MGMTD!\n");
return -1;
}
- vty->mgmt_req_pending = true;
+ vty->mgmt_req_pending_cmd = "MESSAGE_COMMCFG_REQ";
vty->mgmt_num_pending_setcfg = 0;
}
return 0;
}
-int vty_mgmt_send_get_config(struct vty *vty, Mgmtd__DatastoreId datastore,
- const char **xpath_list, int num_req)
+int vty_mgmt_send_get_req(struct vty *vty, bool is_config,
+ Mgmtd__DatastoreId datastore, const char **xpath_list,
+ int num_req)
{
- enum mgmt_result ret;
Mgmtd__YangData yang_data[VTY_MAXCFGCHANGES];
Mgmtd__YangGetDataReq get_req[VTY_MAXCFGCHANGES];
Mgmtd__YangGetDataReq *getreq[VTY_MAXCFGCHANGES];
@@ -3605,56 +4084,40 @@ int vty_mgmt_send_get_config(struct vty *vty, Mgmtd__DatastoreId datastore,
get_req[i].data = &yang_data[i];
getreq[i] = &get_req[i];
}
- ret = mgmt_fe_get_config_data(mgmt_lib_hndl, vty->mgmt_session_id,
- vty->mgmt_req_id, datastore, getreq,
- num_req);
-
- if (ret != MGMTD_SUCCESS) {
- zlog_err(
- "Failed to send GET-CONFIG to MGMTD for req-id %" PRIu64
- ".",
- vty->mgmt_req_id);
+ if (mgmt_fe_send_get_req(mgmt_fe_client, vty->mgmt_session_id,
+ vty->mgmt_req_id, is_config, datastore, getreq,
+ num_req)) {
+ zlog_err("Failed to send GET- to MGMTD for req-id %" PRIu64 ".",
+ vty->mgmt_req_id);
vty_out(vty, "Failed to send GET-CONFIG to MGMTD!\n");
return -1;
}
- vty->mgmt_req_pending = true;
+ vty->mgmt_req_pending_cmd = "MESSAGE_GETCFG_REQ";
return 0;
}
-int vty_mgmt_send_get_data(struct vty *vty, Mgmtd__DatastoreId datastore,
- const char **xpath_list, int num_req)
+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)
{
- enum mgmt_result ret;
- Mgmtd__YangData yang_data[VTY_MAXCFGCHANGES];
- Mgmtd__YangGetDataReq get_req[VTY_MAXCFGCHANGES];
- Mgmtd__YangGetDataReq *getreq[VTY_MAXCFGCHANGES];
- int i;
+ LYD_FORMAT intern_format = result_type;
vty->mgmt_req_id++;
- for (i = 0; i < num_req; i++) {
- mgmt_yang_get_data_req_init(&get_req[i]);
- mgmt_yang_data_init(&yang_data[i]);
-
- yang_data->xpath = (char *)xpath_list[i];
-
- get_req[i].data = &yang_data[i];
- getreq[i] = &get_req[i];
- }
- ret = mgmt_fe_get_data(mgmt_lib_hndl, vty->mgmt_session_id,
- vty->mgmt_req_id, datastore, getreq, num_req);
-
- if (ret != MGMTD_SUCCESS) {
- zlog_err("Failed to send GET-DATA to MGMTD for req-id %" PRIu64
- ".",
- vty->mgmt_req_id);
+ if (mgmt_fe_send_get_data_req(mgmt_fe_client, vty->mgmt_session_id,
+ vty->mgmt_req_id, datastore,
+ intern_format, flags, defaults, xpath)) {
+ zlog_err("Failed to send GET-DATA to MGMTD session-id: %" PRIu64
+ " req-id %" PRIu64 ".",
+ vty->mgmt_session_id, vty->mgmt_req_id);
vty_out(vty, "Failed to send GET-DATA to MGMTD!\n");
return -1;
}
- vty->mgmt_req_pending = true;
+ vty->mgmt_req_pending_cmd = "MESSAGE_GET_DATA_REQ";
+ vty->mgmt_req_pending_data = result_type;
return 0;
}
@@ -3681,8 +4144,8 @@ void vty_init(struct event_loop *master_thread, bool do_command_logging)
install_element(CONFIG_NODE, &log_commands_cmd);
if (do_command_logging) {
- do_log_commands = true;
- do_log_commands_perm = true;
+ vty_log_commands = true;
+ vty_log_commands_perm = true;
}
install_element(ENABLE_NODE, &terminal_monitor_cmd);
@@ -3704,11 +4167,10 @@ void vty_init(struct event_loop *master_thread, bool do_command_logging)
void vty_terminate(void)
{
struct vty *vty;
- struct vty_serv *vtyserv;
- if (mgmt_lib_hndl) {
- mgmt_fe_client_lib_destroy(mgmt_lib_hndl);
- mgmt_lib_hndl = 0;
+ if (mgmt_fe_client) {
+ mgmt_fe_client_destroy(mgmt_fe_client);
+ mgmt_fe_client = 0;
}
memset(vty_cwd, 0x00, sizeof(vty_cwd));
@@ -3730,12 +4192,5 @@ void vty_terminate(void)
vtys_fini(vtysh_sessions);
vtys_init(vtysh_sessions);
- while ((vtyserv = vtyservs_pop(vty_servs))) {
- EVENT_OFF(vtyserv->t_accept);
- close(vtyserv->sock);
- XFREE(MTYPE_VTY_SERV, vtyserv);
- }
-
- vtyservs_fini(vty_servs);
- vtyservs_init(vty_servs);
+ vty_serv_stop();
}
diff --git a/lib/vty.h b/lib/vty.h
index 5114238f6a..a59ac7a652 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -43,6 +43,7 @@ struct json_object;
struct vty_error {
char error_buf[VTY_BUFSIZ];
uint32_t line_num;
+ int cmd_ret;
};
struct vty_cfg_change {
@@ -71,7 +72,11 @@ struct vty {
bool is_paged;
/* Is this vty connect to file or not */
- enum { VTY_TERM, VTY_FILE, VTY_SHELL, VTY_SHELL_SERV } type;
+ enum { VTY_TERM, /* telnet conn or stdin/stdout UI */
+ VTY_FILE, /* reading and writing config files */
+ VTY_SHELL, /* vtysh client side UI */
+ VTY_SHELL_SERV, /* server-side vtysh connection */
+ } type;
/* Node status of this vty */
int node;
@@ -142,7 +147,6 @@ struct vty {
/* Dynamic transaction information. */
bool pending_allowed;
bool pending_commit;
- bool no_implicit_commit;
char *pending_cmds_buf;
size_t pending_cmds_buflen;
size_t pending_cmds_bufpos;
@@ -218,11 +222,16 @@ struct vty {
size_t frame_pos;
char frame[1024];
- uintptr_t mgmt_session_id;
- uint64_t mgmt_client_id;
+ uint64_t mgmt_session_id; /* FE adapter identifies session w/ this */
+ uint64_t mgmt_client_id; /* FE vty client identifies w/ this ID */
uint64_t mgmt_req_id;
- bool mgmt_req_pending;
+ /* set when we have sent mgmtd a *REQ command in response to some vty
+ * CLI command and we are waiting on the reply so we can respond to the
+ * vty user. */
+ const char *mgmt_req_pending_cmd;
+ uintptr_t mgmt_req_pending_data;
bool mgmt_locked_candidate_ds;
+ bool mgmt_locked_running_ds;
};
static inline void vty_push_context(struct vty *vty, int node, uint64_t id)
@@ -298,10 +307,10 @@ static inline void vty_push_context(struct vty *vty, int node, uint64_t id)
#define VTY_CHECK_XPATH \
do { \
- if (vty->type != VTY_FILE && !vty->private_config \
- && vty->xpath_index > 0 \
- && !yang_dnode_exists(vty->candidate_config->dnode, \
- VTY_CURR_XPATH)) { \
+ if (vty->type != VTY_FILE && !vty->private_config && \
+ vty->xpath_index > 0 && \
+ !yang_dnode_exists(vty->candidate_config->dnode, \
+ VTY_CURR_XPATH)) { \
vty_out(vty, \
"Current configuration object was deleted " \
"by another process.\n\n"); \
@@ -335,6 +344,10 @@ struct vty_arg {
#endif
extern struct nb_config *vty_mgmt_candidate_config;
+extern bool vty_log_commands;
+
+extern char const *const mgmt_daemons[];
+extern uint mgmt_daemons_count;
/* Prototypes. */
extern void vty_init(struct event_loop *m, bool do_command_logging);
@@ -361,22 +374,25 @@ 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);
-extern void vty_json_empty(struct vty *vty);
+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
*/
extern void vty_pass_fd(struct vty *vty, int fd);
+extern FILE *vty_open_config(const char *config_file, char *config_default_dir);
extern bool vty_read_config(struct nb_config *config, const char *config_file,
char *config_default_dir);
extern void vty_read_file(struct nb_config *config, FILE *confp);
+extern void vty_read_file_finish(struct vty *vty, struct nb_config *config);
extern void vty_time_print(struct vty *, int);
-extern void vty_serv_sock(const char *, unsigned short, const char *);
+extern void vty_serv_start(const char *, unsigned short, const char *);
+extern void vty_serv_stop(void);
extern void vty_close(struct vty *);
extern char *vty_get_cwd(void);
extern void vty_update_xpath(const char *oldpath, const char *newpath);
extern int vty_config_enter(struct vty *vty, bool private_config,
- bool exclusive);
+ bool exclusive, bool file_lock);
extern void vty_config_exit(struct vty *);
extern int vty_config_node_exit(struct vty *);
extern int vty_shell(struct vty *);
@@ -390,25 +406,26 @@ extern void vty_stdio_close(void);
extern void vty_init_mgmt_fe(void);
extern bool vty_mgmt_fe_enabled(void);
-extern int vty_mgmt_send_config_data(struct vty *vty);
+extern bool vty_mgmt_should_process_cli_apply_changes(struct vty *vty);
+
+extern bool mgmt_vty_read_configs(void);
+extern int vty_mgmt_send_config_data(struct vty *vty, const char *xpath_base,
+ bool implicit_commit);
extern int vty_mgmt_send_commit_config(struct vty *vty, bool validate_only,
bool abort);
-extern int vty_mgmt_send_get_config(struct vty *vty,
- Mgmtd__DatastoreId datastore,
- const char **xpath_list, int num_req);
-extern int vty_mgmt_send_get_data(struct vty *vty, Mgmtd__DatastoreId datastore,
- const char **xpath_list, int num_req);
+extern int vty_mgmt_send_get_req(struct vty *vty, bool is_config,
+ Mgmtd__DatastoreId datastore,
+ const char **xpath_list, int num_req);
+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_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id,
- bool lock);
-extern void vty_mgmt_resume_response(struct vty *vty, bool success);
+ bool lock, bool scok);
+extern void vty_mgmt_resume_response(struct vty *vty, int ret);
static inline bool vty_needs_implicit_commit(struct vty *vty)
{
- return (frr_get_cli_mode() == FRR_CLI_CLASSIC
- ? ((vty->pending_allowed || vty->no_implicit_commit)
- ? false
- : true)
- : false);
+ return frr_get_cli_mode() == FRR_CLI_CLASSIC && !vty->pending_allowed;
}
#ifdef __cplusplus
diff --git a/lib/wheel.c b/lib/wheel.c
index e17995c64a..2520e81d49 100644
--- a/lib/wheel.c
+++ b/lib/wheel.c
@@ -57,7 +57,7 @@ static void wheel_timer_thread(struct event *t)
wheel = EVENT_ARG(t);
- event_execute(wheel->master, wheel_timer_thread_helper, wheel, 0);
+ event_execute(wheel->master, wheel_timer_thread_helper, wheel, 0, NULL);
}
struct timer_wheel *wheel_init(struct event_loop *master, int period,
diff --git a/lib/workqueue.c b/lib/workqueue.c
index fa5d585360..d630af1d1d 100644
--- a/lib/workqueue.c
+++ b/lib/workqueue.c
@@ -42,6 +42,15 @@ static void work_queue_item_free(struct work_queue_item *item)
return;
}
+static inline void work_queue_item_dequeue(struct work_queue *wq,
+ struct work_queue_item *item)
+{
+ assert(wq->item_count > 0);
+
+ wq->item_count--;
+ STAILQ_REMOVE(&wq->items, item, work_queue_item, wq);
+}
+
static void work_queue_item_remove(struct work_queue *wq,
struct work_queue_item *item)
{
@@ -133,6 +142,13 @@ static int work_queue_schedule(struct work_queue *wq, unsigned int delay)
return 0;
}
+static inline void work_queue_item_enqueue(struct work_queue *wq,
+ struct work_queue_item *item)
+{
+ STAILQ_INSERT_TAIL(&wq->items, item, wq);
+ wq->item_count++;
+}
+
void work_queue_add(struct work_queue *wq, void *data)
{
struct work_queue_item *item;
@@ -265,17 +281,14 @@ void work_queue_run(struct event *thread)
do {
ret = wq->spec.workfunc(wq, item->data);
item->ran++;
- } while ((ret == WQ_RETRY_NOW)
- && (item->ran < wq->spec.max_retries));
+ } while (item->ran < wq->spec.max_retries);
switch (ret) {
case WQ_QUEUE_BLOCKED: {
/* decrement item->ran again, cause this isn't an item
- * specific error, and fall through to WQ_RETRY_LATER
+ * specific error, and retry later
*/
item->ran--;
- }
- case WQ_RETRY_LATER: {
goto stats;
}
case WQ_REQUEUE: {
@@ -295,10 +308,6 @@ void work_queue_run(struct event *thread)
titem = item;
break;
}
- case WQ_RETRY_NOW:
- /* a RETRY_NOW that gets here has exceeded max_tries, same as
- * ERROR */
- /* fallthru */
case WQ_SUCCESS:
default: {
work_queue_item_remove(wq, item);
@@ -350,8 +359,7 @@ stats:
/* Is the queue done yet? If it is, call the completion callback. */
if (!work_queue_empty(wq)) {
- if (ret == WQ_RETRY_LATER ||
- ret == WQ_QUEUE_BLOCKED)
+ if (ret == WQ_QUEUE_BLOCKED)
work_queue_schedule(wq, wq->spec.retry);
else
work_queue_schedule(wq, 0);
diff --git a/lib/workqueue.h b/lib/workqueue.h
index 5d84739d5c..a495fe8615 100644
--- a/lib/workqueue.h
+++ b/lib/workqueue.h
@@ -26,9 +26,7 @@ DECLARE_MTYPE(WORK_QUEUE);
/* action value, for use by item processor and item error handlers */
typedef enum {
WQ_SUCCESS = 0,
- WQ_RETRY_NOW, /* retry immediately */
- WQ_RETRY_LATER, /* retry later, cease processing work queue */
- WQ_REQUEUE, /* requeue item, continue processing work queue */
+ WQ_REQUEUE, /* requeue item, continue processing work queue */
WQ_QUEUE_BLOCKED, /* Queue cant be processed at this time.
* Similar to WQ_RETRY_LATER, but doesn't penalise
* the particular item.. */
@@ -117,22 +115,6 @@ work_queue_last_item(struct work_queue *wq)
return STAILQ_LAST(&wq->items, work_queue_item, wq);
}
-static inline void work_queue_item_enqueue(struct work_queue *wq,
- struct work_queue_item *item)
-{
- STAILQ_INSERT_TAIL(&wq->items, item, wq);
- wq->item_count++;
-}
-
-static inline void work_queue_item_dequeue(struct work_queue *wq,
- struct work_queue_item *item)
-{
- assert(wq->item_count > 0);
-
- wq->item_count--;
- STAILQ_REMOVE(&wq->items, item, work_queue_item, wq);
-}
-
/* create a new work queue, of given name.
* user must fill in the spec of the returned work queue before adding
* anything to it
@@ -160,6 +142,7 @@ bool work_queue_is_scheduled(struct work_queue *wq);
/* Helpers, exported for thread.c and command.c */
extern void work_queue_run(struct event *thread);
+/* Function to initialize the workqueue cli */
extern void workqueue_cmd_init(void);
#ifdef __cplusplus
diff --git a/lib/yang.c b/lib/yang.c
index 4dd8654217..d71cb2f498 100644
--- a/lib/yang.c
+++ b/lib/yang.c
@@ -6,12 +6,15 @@
#include <zebra.h>
+#include "darr.h"
#include "log.h"
#include "lib_errors.h"
#include "yang.h"
#include "yang_translator.h"
#include "northbound.h"
+#include "lib/config_paths.h"
+
DEFINE_MTYPE_STATIC(LIB, YANG_MODULE, "YANG module");
DEFINE_MTYPE_STATIC(LIB, YANG_DATA, "YANG data structure");
@@ -37,7 +40,8 @@ static LY_ERR yang_module_imp_clb(const char *mod_name, const char *mod_rev,
struct yang_module_embed *e;
if (!strcmp(mod_name, "ietf-inet-types") ||
- !strcmp(mod_name, "ietf-yang-types"))
+ !strcmp(mod_name, "ietf-yang-types") ||
+ !strcmp(mod_name, "ietf-yang-metadata"))
/* libyang has these built in, don't try finding them here */
return LY_ENOTFOUND;
@@ -97,13 +101,14 @@ RB_GENERATE(yang_modules, yang_module, entry, yang_module_compare)
struct yang_modules yang_modules = RB_INITIALIZER(&yang_modules);
-struct yang_module *yang_module_load(const char *module_name)
+struct yang_module *yang_module_load(const char *module_name,
+ const char **features)
{
struct yang_module *module;
const struct lys_module *module_info;
- module_info =
- ly_ctx_load_module(ly_native_ctx, module_name, NULL, NULL);
+ module_info = ly_ctx_load_module(ly_native_ctx, module_name, NULL,
+ features);
if (!module_info) {
flog_err(EC_LIB_YANG_MODULE_LOAD,
"%s: failed to load data model: %s", __func__,
@@ -127,8 +132,10 @@ struct yang_module *yang_module_load(const char *module_name)
void yang_module_load_all(void)
{
+ static const char * const all_features[] = { "*", NULL };
+
for (size_t i = 0; i < array_size(frr_native_modules); i++)
- yang_module_load(frr_native_modules[i]);
+ yang_module_load(frr_native_modules[i], (const char **)all_features);
}
struct yang_module *yang_module_find(const char *module_name)
@@ -192,6 +199,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;
}
@@ -250,19 +267,44 @@ void yang_snode_get_path(const struct lysc_node *snode,
}
}
-struct lysc_node *yang_find_snode(struct ly_ctx *ly_ctx, const char *xpath,
- uint32_t options)
+LY_ERR yang_resolve_snode_xpath(struct ly_ctx *ly_ctx, const char *xpath,
+ struct lysc_node ***snodes, bool *simple)
{
struct lysc_node *snode;
struct ly_set *set;
LY_ERR err;
- err = lys_find_xpath(ly_native_ctx, NULL, xpath, options, &set);
- if (err || !set->count)
- return NULL;
+ /* lys_find_path will not resolve complex xpaths */
+ snode = (struct lysc_node *)lys_find_path(ly_ctx, NULL, xpath, 0);
+ if (snode) {
+ *darr_append(*snodes) = snode;
+ *simple = true;
+ return LY_SUCCESS;
+ }
+
+ /* Try again to catch complex query cases */
+ err = lys_find_xpath(ly_native_ctx, NULL, xpath, 0, &set);
+ if (err)
+ return err;
+ if (!set->count) {
+ ly_set_free(set, NULL);
+ return LY_ENOTFOUND;
+ }
- snode = set->snodes[0];
+ *simple = false;
+ darr_ensure_i(*snodes, set->count - 1);
+ memcpy(*snodes, set->snodes, set->count * sizeof(set->snodes[0]));
ly_set_free(set, NULL);
+ return LY_SUCCESS;
+}
+
+
+struct lysc_node *yang_find_snode(struct ly_ctx *ly_ctx, const char *xpath,
+ uint32_t options)
+{
+ struct lysc_node *snode;
+
+ snode = (struct lysc_node *)lys_find_path(ly_ctx, NULL, xpath, 0);
return snode;
}
@@ -370,33 +412,10 @@ unsigned int yang_snode_num_keys(const struct lysc_node *snode)
return count;
}
-void yang_dnode_get_path(const struct lyd_node *dnode, char *xpath,
- size_t xpath_len)
+char *yang_dnode_get_path(const struct lyd_node *dnode, char *xpath,
+ size_t xpath_len)
{
- lyd_path(dnode, LYD_PATH_STD, xpath, xpath_len);
-}
-
-const char *yang_dnode_get_schema_name(const struct lyd_node *dnode,
- const char *xpath_fmt, ...)
-{
- if (xpath_fmt) {
- va_list ap;
- char xpath[XPATH_MAXLEN];
-
- va_start(ap, xpath_fmt);
- vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
- va_end(ap);
-
- dnode = yang_dnode_get(dnode, xpath);
- if (!dnode) {
- flog_err(EC_LIB_YANG_DNODE_NOT_FOUND,
- "%s: couldn't find %s", __func__, xpath);
- zlog_backtrace(LOG_ERR);
- abort();
- }
- }
-
- return dnode->schema->name;
+ return lyd_path(dnode, LYD_PATH_STD, xpath, xpath_len);
}
struct lyd_node *yang_dnode_get(const struct lyd_node *dnode, const char *xpath)
@@ -505,6 +524,30 @@ void yang_dnode_iterate(yang_dnode_iter_cb cb, void *arg,
ly_set_free(set, NULL);
}
+uint32_t yang_dnode_count(const struct lyd_node *dnode, const char *xpath_fmt,
+ ...)
+{
+ va_list ap;
+ char xpath[XPATH_MAXLEN];
+ struct ly_set *set;
+ uint32_t count;
+
+ va_start(ap, xpath_fmt);
+ vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
+ va_end(ap);
+
+ if (lyd_find_xpath(dnode, xpath, &set)) {
+ assert(0);
+ return 0;
+ }
+
+ count = set->count;
+
+ ly_set_free(set, NULL);
+
+ return count;
+}
+
bool yang_dnode_is_default(const struct lyd_node *dnode, const char *xpath)
{
const struct lysc_node *snode;
@@ -596,7 +639,8 @@ struct lyd_node *yang_dnode_dup(const struct lyd_node *dnode)
{
struct lyd_node *dup = NULL;
LY_ERR err;
- err = lyd_dup_siblings(dnode, NULL, LYD_DUP_RECURSIVE, &dup);
+ err = lyd_dup_siblings(dnode, NULL,
+ LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &dup);
assert(!err);
return dup;
}
@@ -680,10 +724,122 @@ static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path)
zlog(priority, "libyang: %s", msg);
}
+LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format,
+ const char *data, struct lyd_node **notif)
+{
+ struct lyd_node *tree;
+ struct ly_set *set = NULL;
+ struct ly_in *in = NULL;
+ LY_ERR err;
+
+ err = ly_in_new_memory(data, &in);
+ if (err) {
+ zlog_err("Failed to initialize ly_in: %s", ly_last_errmsg());
+ return err;
+ }
+
+ err = lyd_parse_op(ly_native_ctx, NULL, in, format, LYD_TYPE_NOTIF_YANG,
+ &tree, NULL);
+ ly_in_free(in, 0);
+ if (err) {
+ zlog_err("Failed to parse notification: %s", ly_last_errmsg());
+ return err;
+ }
+
+ err = lyd_find_xpath3(NULL, tree, xpath, NULL, &set);
+ if (err) {
+ zlog_err("Failed to parse notification: %s", ly_last_errmsg());
+ lyd_free_all(tree);
+ return err;
+ }
+ if (set->count == 0) {
+ zlog_err("Notification not found in the parsed tree: %s", xpath);
+ ly_set_free(set, NULL);
+ lyd_free_all(tree);
+ return LY_ENOTFOUND;
+ }
+ *notif = set->dnodes[0];
+ ly_set_free(set, NULL);
+ 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);
+
+ memcpy(dst, buf, count);
+ return count;
+}
+
+LY_ERR yang_print_tree_append(uint8_t **darr, const struct lyd_node *root,
+ LYD_FORMAT format, uint32_t options)
+{
+ LY_ERR err;
+
+ err = lyd_print_clb(yang_print_darr, darr, root, format, options);
+ if (err)
+ zlog_err("Failed to save yang tree: %s", ly_last_errmsg());
+ else if (format != LYD_LYB)
+ *darr_append(*darr) = 0;
+ return err;
+}
+
+uint8_t *yang_print_tree(const struct lyd_node *root, LYD_FORMAT format,
+ uint32_t options)
+{
+ uint8_t *darr = NULL;
+
+ if (yang_print_tree_append(&darr, root, format, options))
+ return NULL;
+ return darr;
+}
+
+char *yang_convert_lyd_format(const char *data, size_t data_len,
+ LYD_FORMAT in_format, LYD_FORMAT out_format,
+ bool shrink)
+{
+ struct lyd_node *tree = NULL;
+ uint32_t options = LYD_PRINT_WD_EXPLICIT | LYD_PRINT_WITHSIBLINGS;
+ uint8_t *result = NULL;
+ LY_ERR err;
+
+ assert(out_format != LYD_LYB);
+
+ if (in_format != LYD_LYB && !MGMT_MSG_VALIDATE_NUL_TERM(data, data_len)) {
+ zlog_err("Corrupt input data, no NUL terminating byte");
+ return NULL;
+ }
+
+ if (in_format == out_format)
+ return darr_strdup((const char *)data);
+
+ err = lyd_parse_data_mem(ly_native_ctx, (const char *)data, in_format,
+ LYD_PARSE_ONLY, 0, &tree);
+
+ if (err) {
+ flog_err_sys(EC_LIB_LIBYANG,
+ "cannot parse input data to convert: %s",
+ ly_last_errmsg());
+ return NULL;
+ }
+
+ if (shrink)
+ options |= LYD_PRINT_SHRINK;
+
+ /* Take a guess at the initial capacity based on input data size */
+ darr_ensure_cap(result, data_len);
+ err = yang_print_tree_append(&result, tree, out_format, options);
+ lyd_free_all(tree);
+ if (err) {
+ darr_free(result);
+ return NULL;
+ }
+ return (char *)result;
+}
+
const char *yang_print_errors(struct ly_ctx *ly_ctx, char *buf, size_t buf_len)
{
struct ly_err_item *ei;
- const char *path;
ei = ly_err_first(ly_ctx);
if (!ei)
@@ -691,18 +847,16 @@ const char *yang_print_errors(struct ly_ctx *ly_ctx, char *buf, size_t buf_len)
strlcpy(buf, "YANG error(s):\n", buf_len);
for (; ei; ei = ei->next) {
- strlcat(buf, " ", buf_len);
+ if (ei->path) {
+ strlcat(buf, " Path: ", buf_len);
+ strlcat(buf, ei->path, buf_len);
+ strlcat(buf, "\n", buf_len);
+ }
+ strlcat(buf, " Error: ", buf_len);
strlcat(buf, ei->msg, buf_len);
strlcat(buf, "\n", buf_len);
}
- path = ly_errpath(ly_ctx);
- if (path) {
- strlcat(buf, " YANG path: ", buf_len);
- strlcat(buf, path, buf_len);
- strlcat(buf, "\n", buf_len);
- }
-
ly_err_clean(ly_ctx, NULL);
return buf;
@@ -723,6 +877,7 @@ struct ly_ctx *yang_ctx_new_setup(bool embedded_modules, bool explicit_compile)
{
struct ly_ctx *ctx = NULL;
const char *yang_models_path = YANG_MODELS_PATH;
+ uint options;
LY_ERR err;
if (access(yang_models_path, R_OK | X_OK)) {
@@ -736,7 +891,7 @@ struct ly_ctx *yang_ctx_new_setup(bool embedded_modules, bool explicit_compile)
YANG_MODELS_PATH);
}
- uint options = LY_CTX_NO_YANGLIBRARY | LY_CTX_DISABLE_SEARCHDIR_CWD;
+ options = LY_CTX_NO_YANGLIBRARY | LY_CTX_DISABLE_SEARCHDIR_CWD;
if (explicit_compile)
options |= LY_CTX_EXPLICIT_COMPILE;
err = ly_ctx_new(yang_models_path, options, &ctx);
@@ -927,3 +1082,274 @@ uint32_t yang_get_list_elements_count(const struct lyd_node *node)
} while (node);
return count;
}
+
+int yang_get_key_preds(char *s, const struct lysc_node *snode,
+ struct yang_list_keys *keys, ssize_t space)
+{
+ const struct lysc_node_leaf *skey;
+ ssize_t len2, len = 0;
+ ssize_t i = 0;
+
+ LY_FOR_KEYS (snode, skey) {
+ assert(i < keys->num);
+ len2 = snprintf(s + len, space - len, "[%s='%s']", skey->name,
+ keys->key[i]);
+ if (len2 > space - len)
+ len = space;
+ else
+ len += len2;
+ i++;
+ }
+
+ assert(i == keys->num);
+ return i;
+}
+
+int yang_get_node_keys(struct lyd_node *node, struct yang_list_keys *keys)
+{
+ struct lyd_node *child = lyd_child(node);
+
+ keys->num = 0;
+ for (; child && lysc_is_key(child->schema); child = child->next) {
+ const char *value = lyd_get_value(child);
+
+ if (!value)
+ return NB_ERR;
+ strlcpy(keys->key[keys->num], value,
+ sizeof(keys->key[keys->num]));
+ keys->num++;
+ }
+ return NB_OK;
+}
+
+/*
+ * ------------------------
+ * Libyang Future Functions
+ * ------------------------
+ *
+ * All these functions are implemented in libyang versions (perhaps unreleased)
+ * beyond what we require currently so we must supply the functionality.
+ */
+
+/*
+ * Safe to remove after libyang v2.1.xxx is required (.144 has a bug so
+ * something > .144) https://github.com/CESNET/libyang/issues/2149
+ */
+LY_ERR yang_lyd_new_list(struct lyd_node_inner *parent,
+ const struct lysc_node *snode,
+ const struct yang_list_keys *list_keys,
+ struct lyd_node **node)
+{
+#if defined(HAVE_LYD_NEW_LIST3) && 0
+ LY_ERR err;
+ const char *keys[LIST_MAXKEYS];
+
+ assert(list_keys->num <= LIST_MAXKEYS);
+ for (int i = 0; i < list_keys->num; i++)
+ keys[i] = list_keys->key[i];
+
+ err = lyd_new_list3(&parent->node, snode->module, snode->name, keys,
+ NULL, 0, node);
+ return err;
+#else
+ struct lyd_node *pnode = &parent->node;
+ const char(*keys)[LIST_MAXKEYLEN] = list_keys->key;
+
+ assert(list_keys->num <= 8);
+ switch (list_keys->num) {
+ case 0:
+ return lyd_new_list(pnode, snode->module, snode->name, false,
+ node);
+ case 1:
+ return lyd_new_list(pnode, snode->module, snode->name, false,
+ node, keys[0]);
+ case 2:
+ return lyd_new_list(pnode, snode->module, snode->name, false,
+ node, keys[0], keys[1]);
+ case 3:
+ return lyd_new_list(pnode, snode->module, snode->name, false,
+ node, keys[0], keys[1], keys[2]);
+ case 4:
+ return lyd_new_list(pnode, snode->module, snode->name, false,
+ node, keys[0], keys[1], keys[2], keys[3]);
+ case 5:
+ return lyd_new_list(pnode, snode->module, snode->name, false,
+ node, keys[0], keys[1], keys[2], keys[3],
+ keys[4]);
+ case 6:
+ return lyd_new_list(pnode, snode->module, snode->name, false,
+ node, keys[0], keys[1], keys[2], keys[3],
+ keys[4], keys[5]);
+ case 7:
+ return lyd_new_list(pnode, snode->module, snode->name, false,
+ node, keys[0], keys[1], keys[2], keys[3],
+ keys[4], keys[5], keys[6]);
+ case 8:
+ return lyd_new_list(pnode, snode->module, snode->name, false,
+ node, keys[0], keys[1], keys[2], keys[3],
+ keys[4], keys[5], keys[6], keys[7]);
+ }
+ _Static_assert(LIST_MAXKEYS == 8, "max key mismatch in switch unroll");
+ /*NOTREACHED*/
+ return LY_EINVAL;
+#endif
+}
+
+
+/*
+ * Safe to remove after libyang v2.1.144 is required
+ */
+LY_ERR yang_lyd_trim_xpath(struct lyd_node **root, const char *xpath)
+{
+ LY_ERR err;
+#ifdef HAVE_LYD_TRIM_XPATH
+ err = lyd_trim_xpath(root, xpath, NULL);
+ if (err) {
+ flog_err_sys(EC_LIB_LIBYANG,
+ "cannot obtain specific result for xpath \"%s\": %s",
+ xpath, yang_ly_strerrcode(err));
+ return err;
+ }
+ return LY_SUCCESS;
+#else
+ struct lyd_node *node, *sib;
+ struct lyd_node **remove = NULL;
+ struct ly_set *set = NULL;
+ uint32_t i;
+
+ *root = lyd_first_sibling(*root);
+
+ err = lyd_find_xpath3(NULL, *root, xpath, NULL, &set);
+ if (err) {
+ flog_err_sys(EC_LIB_LIBYANG,
+ "cannot obtain specific result for xpath \"%s\": %s",
+ xpath, yang_ly_strerrcode(err));
+ return err;
+ }
+ /*
+ * Mark keepers and sweep deleting non-keepers.
+ *
+ * NOTE: We assume the data-nodes have NULL priv pointers and use that
+ * for our mark.
+ */
+
+ /* Mark */
+ for (i = 0; i < set->count; i++) {
+ for (node = set->dnodes[i]; node; node = &node->parent->node) {
+ if (node->priv)
+ break;
+ if (node == set->dnodes[i])
+ node->priv = (void *)2;
+ else
+ node->priv = (void *)1;
+ }
+ }
+
+ darr_ensure_cap(remove, 128);
+ LY_LIST_FOR(*root, sib) {
+ LYD_TREE_DFS_BEGIN (sib, node) {
+ /*
+ * If this is a direct matching node then include its
+ * subtree which won't be marked and would otherwise
+ * be removed.
+ */
+ if (node->priv == (void *)2)
+ LYD_TREE_DFS_continue = 1;
+ else if (!node->priv) {
+ *darr_append(remove) = node;
+ LYD_TREE_DFS_continue = 1;
+ }
+ LYD_TREE_DFS_END(sib, node);
+ }
+ }
+ darr_foreach_i (remove, i) {
+ if (remove[i] == *root)
+ *root = (*root)->next;
+ lyd_free_tree(remove[i]);
+ }
+ darr_free(remove);
+
+ ly_set_free(set, NULL);
+
+ return LY_SUCCESS;
+#endif
+}
+
+/*
+ * Safe to remove after libyang v2.1.128 is required
+ */
+const char *yang_ly_strerrcode(LY_ERR err)
+{
+#ifdef HAVE_LY_STRERRCODE
+ return ly_strerrcode(err);
+#else
+ switch (err) {
+ case LY_SUCCESS:
+ return "ok";
+ case LY_EMEM:
+ return "out of memory";
+ case LY_ESYS:
+ return "system error";
+ case LY_EINVAL:
+ return "invalid value given";
+ case LY_EEXIST:
+ return "item exists";
+ case LY_ENOTFOUND:
+ return "item not found";
+ case LY_EINT:
+ return "operation interrupted";
+ case LY_EVALID:
+ return "validation failed";
+ case LY_EDENIED:
+ return "access denied";
+ case LY_EINCOMPLETE:
+ return "incomplete";
+ case LY_ERECOMPILE:
+ return "compile error";
+ case LY_ENOT:
+ return "not";
+ case LY_EPLUGIN:
+ case LY_EOTHER:
+ return "other";
+ default:
+ return "unknown";
+ }
+#endif
+}
+
+/*
+ * Safe to remove after libyang v2.1.128 is required
+ */
+const char *yang_ly_strvecode(LY_VECODE vecode)
+{
+#ifdef HAVE_LY_STRVECODE
+ return ly_strvecode(vecode);
+#else
+ switch (vecode) {
+ case LYVE_SUCCESS:
+ return "";
+ case LYVE_SYNTAX:
+ return "syntax";
+ case LYVE_SYNTAX_YANG:
+ return "yang-syntax";
+ case LYVE_SYNTAX_YIN:
+ return "yin-syntax";
+ case LYVE_REFERENCE:
+ return "reference";
+ case LYVE_XPATH:
+ return "xpath";
+ case LYVE_SEMANTICS:
+ return "semantics";
+ case LYVE_SYNTAX_XML:
+ return "xml-syntax";
+ case LYVE_SYNTAX_JSON:
+ return "json-syntax";
+ case LYVE_DATA:
+ return "data";
+ case LYVE_OTHER:
+ return "other";
+ default:
+ return "unknown";
+ }
+#endif
+}
diff --git a/lib/yang.h b/lib/yang.h
index 37369c09bf..85be38bf81 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;
@@ -112,10 +109,16 @@ extern struct yang_modules yang_modules;
* module_name
* Name of the YANG module.
*
+ * features
+ * NULL-terminated array of feature names to enable.
+ * If NULL, all features are disabled.
+ * To enable all features, use ["*", NULL].
+ *
* Returns:
* Pointer to newly created YANG module.
*/
-extern struct yang_module *yang_module_load(const char *module_name);
+extern struct yang_module *yang_module_load(const char *module_name,
+ const char **features);
/*
* Load all FRR native YANG models.
@@ -317,30 +320,16 @@ extern unsigned int yang_snode_num_keys(const struct lysc_node *snode);
* libyang data node to be processed.
*
* xpath
- * Pointer to previously allocated buffer.
+ * Pointer to previously allocated buffer or NULL.
*
* xpath_len
- * Size of the xpath buffer.
- */
-extern void yang_dnode_get_path(const struct lyd_node *dnode, char *xpath,
- size_t xpath_len);
-
-/*
- * Return the schema name of the given libyang data node.
+ * Size of the xpath buffer if xpath non-NULL.
*
- * dnode
- * libyang data node.
- *
- * xpath_fmt
- * Optional XPath expression (absolute or relative) to specify a different
- * data node to operate on in the same data tree.
- *
- * Returns:
- * Schema name of the libyang data node.
+ * If xpath is NULL, the returned string (if non-NULL) needs to be free()d by
+ * the caller.
*/
-extern const char *yang_dnode_get_schema_name(const struct lyd_node *dnode,
- const char *xpath_fmt, ...)
- PRINTFRR(2, 3);
+extern char *yang_dnode_get_path(const struct lyd_node *dnode, char *xpath,
+ size_t xpath_len);
/*
* Find a libyang data node by its YANG data path.
@@ -435,6 +424,21 @@ void yang_dnode_iterate(yang_dnode_iter_cb cb, void *arg,
...) PRINTFRR(4, 5);
/*
+ * Count the number of data nodes that satisfy an XPath query.
+ *
+ * dnode
+ * Base libyang data node to operate on.
+ *
+ * xpath_fmt
+ * XPath expression (absolute or relative).
+ *
+ * ...
+ * any parameters for xpath_fmt.
+ */
+uint32_t yang_dnode_count(const struct lyd_node *dnode, const char *xpath_fmt,
+ ...) PRINTFRR(2, 3);
+
+/*
* Check if the libyang data node contains a default value. Non-presence
* containers are assumed to always contain a default value.
*
@@ -601,6 +605,69 @@ extern struct ly_ctx *yang_ctx_new_setup(bool embedded_modules,
extern void yang_debugging_set(bool enable);
/*
+ * Parse a YANG notification.
+ *
+ * Args:
+ * xpath: xpath of notification.
+ * format: LYD_FORMAT of input data.
+ * data: input data.
+ * notif: pointer to the libyang data tree to store the parsed notification.
+ * If the notification is not on the top level of the yang model,
+ * the pointer to the notification node is still returned, but it's
+ * part of the full data tree with all its parents.
+ */
+extern LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format,
+ const char *data, struct lyd_node **notif);
+
+/*
+ * "Print" the yang tree in `root` into dynamic sized array.
+ *
+ * Args:
+ * root: root of the subtree to "print" along with siblings.
+ * format: LYD_FORMAT of output (see lyd_print_mem)
+ * options: printing options (see lyd_print_mem)
+ *
+ * Return:
+ * A darr dynamic array with the "printed" output or NULL on failure.
+ */
+extern uint8_t *yang_print_tree(const struct lyd_node *root, LYD_FORMAT format,
+ uint32_t options);
+
+
+/**
+ * yang_convert_lyd_format() - convert one libyang format to darr string.
+ * @data: data to convert.
+ * @data_len: length of the data.
+ * @in_format: format of the data.
+ * @out_format: format to return.
+ * @shrink: true to avoid pretty printing.
+ *
+ * Return:
+ * A darr based string or NULL for error.
+ */
+extern char *yang_convert_lyd_format(const char *data, size_t msg_len,
+ LYD_FORMAT in_format,
+ LYD_FORMAT out_format, bool shrink);
+
+/*
+ * "Print" the yang tree in `root` into an existing dynamic sized array.
+ *
+ * This function does not initialize or free the dynamic array, the array can
+ * already existing data, the tree will be appended to this data.
+ *
+ * Args:
+ * darr: existing `uint8_t *`, dynamic array.
+ * root: root of the subtree to "print" along with siblings.
+ * format: LYD_FORMAT of output (see lyd_print_mem)
+ * options: printing options (see lyd_print_mem)
+ *
+ * Return:
+ * LY_ERR from underlying calls.
+ */
+extern LY_ERR yang_print_tree_append(uint8_t **darr, const struct lyd_node *root,
+ LYD_FORMAT format, uint32_t options);
+
+/*
* Print libyang error messages into the provided buffer.
*
* ly_ctx
@@ -693,6 +760,44 @@ bool yang_is_last_list_dnode(const struct lyd_node *dnode);
/* API to check if the given node is last node in the data tree level */
bool yang_is_last_level_dnode(const struct lyd_node *dnode);
+/* Create a YANG predicate string based on the keys */
+extern int yang_get_key_preds(char *s, const struct lysc_node *snode,
+ struct yang_list_keys *keys, ssize_t space);
+
+/* Get YANG keys from an existing dnode */
+extern int yang_get_node_keys(struct lyd_node *node, struct yang_list_keys *keys);
+
+/**
+ * yang_resolve_snodes() - Resolve an XPath to matching schema nodes.
+ * @ly_ctx: libyang context to operate on.
+ * @xpath: the path or XPath to resolve.
+ * @snodes: [OUT] pointer for resulting dynamic array (darr) of schema node
+ * pointers.
+ * @simple: [OUT] indicates if @xpath was resolvable simply or not. Non-simple
+ * means that the @xpath is not a simple path and utilizes XPath 1.0
+ * functionality beyond simple key predicates.
+ *
+ * This function can be used to find the schema node (or nodes) that correspond
+ * to a given @xpath. If the @xpath includes non-key predicates (e.g., using
+ * functions) then @simple will be set to false, and @snodes may contain more
+ * than a single schema node.
+ *
+ * Return: a libyang error or LY_SUCCESS.
+ */
+extern LY_ERR yang_resolve_snode_xpath(struct ly_ctx *ly_ctx, const char *xpath,
+ struct lysc_node ***snodes, bool *simple);
+
+/*
+ * Libyang future functions
+ */
+extern const char *yang_ly_strerrcode(LY_ERR err);
+extern const char *yang_ly_strvecode(LY_VECODE vecode);
+extern LY_ERR yang_lyd_new_list(struct lyd_node_inner *parent,
+ const struct lysc_node *snode,
+ const struct yang_list_keys *keys,
+ struct lyd_node **nodes);
+extern LY_ERR yang_lyd_trim_xpath(struct lyd_node **rootp, const char *xpath);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/yang_translator.c b/lib/yang_translator.c
index eae7577a0d..005f6422f3 100644
--- a/lib/yang_translator.c
+++ b/lib/yang_translator.c
@@ -146,7 +146,7 @@ struct yang_translator *yang_translator_load(const char *path)
*/
assert(dnode);
- family = yang_dnode_get_string(dnode, "./family");
+ family = yang_dnode_get_string(dnode, "family");
translator = yang_translator_find(family);
if (translator != NULL) {
flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
@@ -182,7 +182,7 @@ struct yang_translator *yang_translator_load(const char *path)
tmodule =
XCALLOC(MTYPE_YANG_TRANSLATOR_MODULE, sizeof(*tmodule));
- module_name = yang_dnode_get_string(set->dnodes[i], "./name");
+ module_name = yang_dnode_get_string(set->dnodes[i], "name");
tmodule->module = ly_ctx_load_module(translator->ly_ctx,
module_name, NULL, NULL);
if (!tmodule->module) {
@@ -233,7 +233,7 @@ struct yang_translator *yang_translator_load(const char *path)
const struct lysc_node *snode_custom, *snode_native;
xpath_custom =
- yang_dnode_get_string(set->dnodes[i], "./custom");
+ yang_dnode_get_string(set->dnodes[i], "custom");
snode_custom =
yang_find_snode(translator->ly_ctx, xpath_custom, 0);
@@ -246,7 +246,7 @@ struct yang_translator *yang_translator_load(const char *path)
}
xpath_native =
- yang_dnode_get_string(set->dnodes[i], "./native");
+ yang_dnode_get_string(set->dnodes[i], "native");
snode_native = yang_find_snode(ly_native_ctx, xpath_native, 0);
if (!snode_native) {
flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
diff --git a/lib/yang_wrappers.c b/lib/yang_wrappers.c
index dc049a374a..4e49a129c2 100644
--- a/lib/yang_wrappers.c
+++ b/lib/yang_wrappers.c
@@ -168,10 +168,9 @@ struct yang_data *yang_data_new_dec64(const char *xpath, double value)
double yang_dnode_get_dec64(const struct lyd_node *dnode, const char *xpath_fmt,
...)
{
- const double denom[19] = {1e0, 1e-1, 1e-2, 1e-3, 1e-4,
- 1e-5, 1e-6, 1e-7, 1e-8, 1e-9,
- 1e-10, 1e-11, 1e-12, 1e-13, 1e-14,
- 1e-15, 1e-16, 1e-17, 1e-18};
+ const double denom[19] = { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6,
+ 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13,
+ 1e14, 1e15, 1e16, 1e17, 1e18 };
const struct lysc_type_dec *dectype;
const struct lyd_value *dvalue;
@@ -179,7 +178,7 @@ double yang_dnode_get_dec64(const struct lyd_node *dnode, const char *xpath_fmt,
dectype = (const struct lysc_type_dec *)dvalue->realtype;
assert(dectype->basetype == LY_TYPE_DEC64);
assert(dectype->fraction_digits < sizeof(denom) / sizeof(*denom));
- return (double)dvalue->dec64 * denom[dectype->fraction_digits];
+ return (double)dvalue->dec64 / denom[dectype->fraction_digits];
}
double yang_get_default_dec64(const char *xpath_fmt, ...)
@@ -1020,30 +1019,68 @@ void yang_str2mac(const char *value, struct ethaddr *mac)
(void)prefix_str2mac(value, mac);
}
-struct yang_data *yang_data_new_date_and_time(const char *xpath, time_t time)
+void yang_dnode_get_mac(struct ethaddr *mac, const struct lyd_node *dnode,
+ const char *xpath_fmt, ...)
{
- struct tm tm;
- char timebuf[MONOTIME_STRLEN];
- struct timeval _time, time_real;
- char *ts_dot;
- uint16_t buflen;
+ const char *canon = YANG_DNODE_XPATH_GET_CANON(dnode, xpath_fmt);
+ (void)prefix_str2mac(canon, mac);
+}
+
+struct yang_data *yang_data_new_date_and_time(const char *xpath, time_t time, bool is_monotime)
+{
+ struct yang_data *yd;
+ char *times = NULL;
+
+ if (is_monotime) {
+ struct timeval _time = { time, 0 };
+ struct timeval time_real;
+
+ 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);
- _time.tv_sec = time;
- _time.tv_usec = 0;
- monotime_to_realtime(&_time, &time_real);
+ return yd;
+}
- gmtime_r(&time_real.tv_sec, &tm);
+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;
- /* rfc-3339 format */
- strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%S", &tm);
- buflen = strlen(timebuf);
- ts_dot = timebuf + buflen;
+ err = ly_time_str2ts(canon, &ts);
+ assert(!err);
- /* microseconds and appends Z */
- snprintfrr(ts_dot, sizeof(timebuf) - buflen, ".%06luZ",
- (unsigned long)time_real.tv_usec);
+ return ts;
+}
- return yang_data_new(xpath, timebuf);
+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;
+
+ err = ly_time_str2time(canon, &time, NULL);
+ assert(!err);
+
+ return time;
+}
+
+float yang_dnode_get_bandwidth_ieee_float32(const struct lyd_node *dnode,
+ const char *xpath_fmt, ...)
+{
+ const char *canon = YANG_DNODE_XPATH_GET_CANON(dnode, xpath_fmt);
+ float value;
+
+ assert(sscanf(canon, "%a", &value) == 1);
+
+ return value;
}
const char *yang_nexthop_type2str(uint32_t ntype)
diff --git a/lib/yang_wrappers.h b/lib/yang_wrappers.h
index 06e05872e3..d3d841995b 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
@@ -195,10 +196,24 @@ extern void yang_get_default_ip(struct ipaddr *var, const char *xpath_fmt, ...)
extern struct yang_data *yang_data_new_mac(const char *xpath,
const struct ethaddr *mac);
extern void yang_str2mac(const char *value, struct ethaddr *mac);
+extern void yang_dnode_get_mac(struct ethaddr *mac, const struct lyd_node *dnode,
+ const char *xpath_fmt, ...) PRINTFRR(3, 4);
/*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,
+ const char *xpath_fmt, ...)
+ PRINTFRR(2, 3);
/* nexthop enum2str */
extern const char *yang_nexthop_type2str(uint32_t ntype);
diff --git a/lib/zclient.c b/lib/zclient.c
index 8526cbfaa1..4cbd04c116 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -41,8 +41,20 @@ static void zclient_event(enum zclient_event, struct zclient *);
static void zebra_interface_if_set_value(struct stream *s,
struct interface *ifp);
-struct zclient_options zclient_options_default = {.receive_notify = false,
- .synchronous = false};
+const struct zclient_options zclient_options_default = {
+ .synchronous = false,
+ .auxiliary = false,
+};
+
+const struct zclient_options zclient_options_sync = {
+ .synchronous = true,
+ .auxiliary = true,
+};
+
+const struct zclient_options zclient_options_auxiliary = {
+ .synchronous = false,
+ .auxiliary = true,
+};
struct sockaddr_storage zclient_addr;
socklen_t zclient_addr_len;
@@ -52,7 +64,7 @@ static int zclient_debug;
/* Allocate zclient structure. */
struct zclient *zclient_new(struct event_loop *master,
- struct zclient_options *opt,
+ const struct zclient_options *opt,
zclient_handler *const *handlers, size_t n_handlers)
{
struct zclient *zclient;
@@ -69,8 +81,8 @@ struct zclient *zclient_new(struct event_loop *master,
zclient->handlers = handlers;
zclient->n_handlers = n_handlers;
- zclient->receive_notify = opt->receive_notify;
zclient->synchronous = opt->synchronous;
+ zclient->auxiliary = opt->auxiliary;
return zclient;
}
@@ -180,14 +192,14 @@ void zclient_stop(struct zclient *zclient)
for (afi = AFI_IP; afi < AFI_MAX; afi++) {
for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
- vrf_bitmap_free(zclient->redist[afi][i]);
+ vrf_bitmap_free(&zclient->redist[afi][i]);
zclient->redist[afi][i] = VRF_BITMAP_NULL;
}
redist_del_instance(
&zclient->mi_redist[afi][zclient->redist_default],
zclient->instance);
- vrf_bitmap_free(zclient->default_information[afi]);
+ vrf_bitmap_free(&zclient->default_information[afi]);
zclient->default_information[afi] = VRF_BITMAP_NULL;
}
}
@@ -270,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;
@@ -392,10 +405,6 @@ enum zclient_send_status zclient_send_hello(struct zclient *zclient)
stream_putc(s, zclient->redist_default);
stream_putw(s, zclient->instance);
stream_putl(s, zclient->session_id);
- if (zclient->receive_notify)
- stream_putc(s, 1);
- else
- stream_putc(s, 0);
if (zclient->synchronous)
stream_putc(s, 1);
else
@@ -494,7 +503,7 @@ void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id)
/* Set unwanted redistribute route. */
for (afi = AFI_IP; afi < AFI_MAX; afi++)
- vrf_bitmap_set(zclient->redist[afi][zclient->redist_default],
+ vrf_bitmap_set(&zclient->redist[afi][zclient->redist_default],
vrf_id);
/* Flush all redistribute request. */
@@ -524,15 +533,15 @@ void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id)
/* Resend all redistribute request. */
for (afi = AFI_IP; afi < AFI_MAX; afi++) {
for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
- if (i != zclient->redist_default
- && vrf_bitmap_check(zclient->redist[afi][i],
- vrf_id))
+ if (i != zclient->redist_default &&
+ vrf_bitmap_check(&zclient->redist[afi][i], vrf_id))
zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD,
zclient, afi, i, 0,
vrf_id);
/* If default information is needed. */
- if (vrf_bitmap_check(zclient->default_information[afi], vrf_id))
+ if (vrf_bitmap_check(&zclient->default_information[afi],
+ vrf_id))
zebra_redistribute_default_send(
ZEBRA_REDISTRIBUTE_DEFAULT_ADD, zclient, afi,
vrf_id);
@@ -561,7 +570,7 @@ void zclient_send_dereg_requests(struct zclient *zclient, vrf_id_t vrf_id)
/* Set unwanted redistribute route. */
for (afi = AFI_IP; afi < AFI_MAX; afi++)
- vrf_bitmap_unset(zclient->redist[afi][zclient->redist_default],
+ vrf_bitmap_unset(&zclient->redist[afi][zclient->redist_default],
vrf_id);
/* Flush all redistribute request. */
@@ -591,15 +600,15 @@ void zclient_send_dereg_requests(struct zclient *zclient, vrf_id_t vrf_id)
/* Flush all redistribute request. */
for (afi = AFI_IP; afi < AFI_MAX; afi++) {
for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
- if (i != zclient->redist_default
- && vrf_bitmap_check(zclient->redist[afi][i],
- vrf_id))
+ if (i != zclient->redist_default &&
+ vrf_bitmap_check(&zclient->redist[afi][i], vrf_id))
zebra_redistribute_send(
ZEBRA_REDISTRIBUTE_DELETE, zclient, afi,
i, 0, vrf_id);
/* If default information is needed. */
- if (vrf_bitmap_check(zclient->default_information[afi], vrf_id))
+ if (vrf_bitmap_check(&zclient->default_information[afi],
+ vrf_id))
zebra_redistribute_default_send(
ZEBRA_REDISTRIBUTE_DEFAULT_DELETE, zclient, afi,
vrf_id);
@@ -726,7 +735,7 @@ void zclient_init(struct zclient *zclient, int redist_default,
/* Clear redistribution flags. */
for (afi = AFI_IP; afi < AFI_MAX; afi++)
for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
- zclient->redist[afi][i] = vrf_bitmap_init();
+ vrf_bitmap_init(&zclient->redist[afi][i]);
/* Set unwanted redistribute route. bgpd does not need BGP route
redistribution. */
@@ -738,7 +747,7 @@ void zclient_init(struct zclient *zclient, int redist_default,
instance);
/* Set default-information redistribute to zero. */
- zclient->default_information[afi] = vrf_bitmap_init();
+ vrf_bitmap_init(&zclient->default_information[afi]);
}
if (zclient_debug)
@@ -891,7 +900,7 @@ static int zapi_nexthop_cmp_no_labels(const struct zapi_nexthop *next1,
&next2->gate);
if (ret != 0)
return ret;
- /* Intentional Fall-Through */
+ fallthrough;
case NEXTHOP_TYPE_IFINDEX:
if (next1->ifindex < next2->ifindex)
return -1;
@@ -1061,10 +1070,11 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh,
sizeof(struct seg6local_context));
}
- if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_SEG6))
- stream_write(s, &api_nh->seg6_segs,
- sizeof(struct in6_addr));
-
+ if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_SEG6)) {
+ stream_putc(s, api_nh->seg_num);
+ stream_put(s, &api_nh->seg6_segs[0],
+ api_nh->seg_num * sizeof(struct in6_addr));
+ }
done:
return ret;
}
@@ -1430,9 +1440,18 @@ int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh,
sizeof(struct seg6local_context));
}
- if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6))
- STREAM_GET(&api_nh->seg6_segs, s,
- sizeof(struct in6_addr));
+ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6)) {
+ STREAM_GETC(s, api_nh->seg_num);
+ if (api_nh->seg_num > SRV6_MAX_SIDS) {
+ flog_err(EC_LIB_ZAPI_ENCODE,
+ "%s: invalid number of SRv6 Segs (%u)",
+ __func__, api_nh->seg_num);
+ return -1;
+ }
+
+ STREAM_GET(&api_nh->seg6_segs[0], s,
+ api_nh->seg_num * sizeof(struct in6_addr));
+ }
/* Success */
ret = 0;
@@ -1611,30 +1630,237 @@ static void zapi_encode_prefix(struct stream *s, struct prefix *p,
stream_put(s, &p->u.prefix, prefix_blen(p));
}
-int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule)
+static bool zapi_decode_prefix(struct stream *s, struct prefix *p)
{
- stream_reset(s);
- zclient_create_header(s, cmd, zrule->vrf_id);
+ STREAM_GETC(s, p->family);
+ STREAM_GETC(s, p->prefixlen);
+ STREAM_GET(&(p->u.prefix), s, prefix_blen(p));
+ return true;
+
+stream_failure:
+ return false;
+}
+
+static void zapi_encode_sockunion(struct stream *s, const union sockunion *su)
+{
+ int family = sockunion_family(su);
+ size_t addrlen = family2addrsize(family);
+
+ /*
+ * Must know length to encode
+ */
+ assert(addrlen);
+
+ stream_putc(s, (uint8_t)family);
+
+ stream_write(s, sockunion_get_addr(su), addrlen);
+}
+
+static bool zapi_decode_sockunion(struct stream *s, union sockunion *su)
+{
+ uint8_t family;
+ size_t addrlen;
+ uint8_t buf[sizeof(union sockunion)];
+
+ memset(su, 0, sizeof(*su));
+
+ STREAM_GETC(s, family);
+ sockunion_family(su) = family;
+
+ addrlen = family2addrsize(family);
+ if (!addrlen)
+ return false;
+
+ if (addrlen > sizeof(buf))
+ return false;
+
+ STREAM_GET(buf, s, addrlen);
+ sockunion_set(su, family, buf, addrlen);
+ return true;
+
+stream_failure:
+ return false;
+}
+
+/*
+ * Encode filter subsection of pbr_rule
+ */
+static void zapi_pbr_rule_filter_encode(struct stream *s, struct pbr_filter *f)
+{
+ assert(f->src_ip.family == f->dst_ip.family);
+ assert((f->src_ip.family == AF_INET) || (f->src_ip.family == AF_INET6));
+
+ stream_putl(s, f->filter_bm);
+
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_IP_PROTOCOL))
+ stream_putc(s, f->ip_proto);
+
+ /* addresses */
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_SRC_IP))
+ zapi_encode_prefix(s, &f->src_ip, f->src_ip.family);
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DST_IP))
+ zapi_encode_prefix(s, &f->dst_ip, f->dst_ip.family);
+
+ /* port numbers */
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_SRC_PORT))
+ stream_putw(s, f->src_port);
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DST_PORT))
+ stream_putw(s, f->dst_port);
+
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DSCP))
+ stream_putc(s, f->dsfield & PBR_DSFIELD_DSCP);
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_ECN))
+ stream_putc(s, f->dsfield & PBR_DSFIELD_ECN);
+
+ /* vlan */
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_PCP))
+ stream_putc(s, f->pcp);
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_VLAN_ID))
+ stream_putw(s, f->vlan_id);
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_VLAN_FLAGS))
+ stream_putw(s, f->vlan_flags);
+
+
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_FWMARK))
+ stream_putl(s, f->fwmark);
+}
+
+static bool zapi_pbr_rule_filter_decode(struct stream *s, struct pbr_filter *f)
+{
+ uint8_t dscp = 0;
+ uint8_t ecn = 0;
+
+ STREAM_GETL(s, f->filter_bm);
+
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_IP_PROTOCOL))
+ STREAM_GETC(s, f->ip_proto);
+
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_SRC_IP))
+ if (!zapi_decode_prefix(s, &(f->src_ip)))
+ goto stream_failure;
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DST_IP))
+ if (!zapi_decode_prefix(s, &(f->dst_ip)))
+ goto stream_failure;
+
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_SRC_PORT))
+ STREAM_GETW(s, f->src_port);
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DST_PORT))
+ STREAM_GETW(s, f->dst_port);
+
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DSCP))
+ STREAM_GETC(s, dscp);
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_ECN))
+ STREAM_GETC(s, ecn);
+ f->dsfield = (dscp & PBR_DSFIELD_DSCP) | (ecn & PBR_DSFIELD_ECN);
+
+ /* vlan */
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_PCP))
+ STREAM_GETC(s, f->pcp);
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_VLAN_ID))
+ STREAM_GETW(s, f->vlan_id);
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_VLAN_FLAGS))
+ STREAM_GETW(s, f->vlan_flags);
+
+ if (CHECK_FLAG(f->filter_bm, PBR_FILTER_FWMARK))
+ STREAM_GETL(s, f->fwmark);
+
+ return true;
+
+stream_failure:
+ return false;
+}
+
+static void zapi_pbr_rule_action_encode(struct stream *s, struct pbr_action *a)
+{
+ stream_putl(s, a->flags);
+
+ if (CHECK_FLAG(a->flags, PBR_ACTION_TABLE))
+ stream_putl(s, a->table);
+ if (CHECK_FLAG(a->flags, PBR_ACTION_QUEUE_ID))
+ stream_putl(s, a->queue_id);
+
+ /* L3 */
+ if (CHECK_FLAG(a->flags, PBR_ACTION_SRC_IP))
+ zapi_encode_sockunion(s, &a->src_ip);
+ if (CHECK_FLAG(a->flags, PBR_ACTION_DST_IP))
+ zapi_encode_sockunion(s, &a->dst_ip);
+ if (CHECK_FLAG(a->flags, PBR_ACTION_SRC_PORT))
+ stream_putw(s, a->src_port);
+ if (CHECK_FLAG(a->flags, PBR_ACTION_DST_PORT))
+ stream_putw(s, a->dst_port);
+
+ if (CHECK_FLAG(a->flags, PBR_ACTION_DSCP))
+ stream_putc(s, a->dscp & PBR_DSFIELD_DSCP);
+ if (CHECK_FLAG(a->flags, PBR_ACTION_ECN))
+ stream_putc(s, a->ecn & PBR_DSFIELD_ECN);
+
+ /* L2 */
+ if (CHECK_FLAG(a->flags, PBR_ACTION_PCP))
+ stream_putc(s, a->pcp);
+ if (CHECK_FLAG(a->flags, PBR_ACTION_VLAN_ID))
+ stream_putw(s, a->vlan_id);
+}
+
+static bool zapi_pbr_rule_action_decode(struct stream *s, struct pbr_action *a)
+{
+ STREAM_GETL(s, a->flags);
+
+ if (CHECK_FLAG(a->flags, PBR_ACTION_TABLE))
+ STREAM_GETL(s, a->table);
+ if (CHECK_FLAG(a->flags, PBR_ACTION_QUEUE_ID))
+ STREAM_GETL(s, a->queue_id);
+
+ /* L3 */
+ if (CHECK_FLAG(a->flags, PBR_ACTION_SRC_IP)) {
+ if (!zapi_decode_sockunion(s, &(a->src_ip)))
+ goto stream_failure;
+ }
+ if (CHECK_FLAG(a->flags, PBR_ACTION_DST_IP))
+ if (!zapi_decode_sockunion(s, &(a->dst_ip)))
+ goto stream_failure;
+
+ if (CHECK_FLAG(a->flags, PBR_ACTION_SRC_PORT))
+ STREAM_GETW(s, a->src_port);
+ if (CHECK_FLAG(a->flags, PBR_ACTION_DST_PORT))
+ STREAM_GETW(s, a->dst_port);
+
+ if (CHECK_FLAG(a->flags, PBR_ACTION_DSCP)) {
+ STREAM_GETC(s, a->dscp);
+ a->dscp &= PBR_DSFIELD_DSCP;
+ }
+ if (CHECK_FLAG(a->flags, PBR_ACTION_ECN)) {
+ STREAM_GETC(s, a->ecn);
+ a->ecn &= PBR_DSFIELD_ECN;
+ }
+
+ /* L2 */
+ if (CHECK_FLAG(a->flags, PBR_ACTION_PCP))
+ STREAM_GETC(s, a->pcp);
+ if (CHECK_FLAG(a->flags, PBR_ACTION_VLAN_ID))
+ STREAM_GETW(s, a->vlan_id);
+ return true;
+
+stream_failure:
+ return false;
+}
+
+int zapi_pbr_rule_encode(struct stream *s, struct pbr_rule *r)
+{
/*
- * We are sending one item at a time at the moment
+ * PBR record count is always 1
*/
stream_putl(s, 1);
- stream_putl(s, zrule->seq);
- stream_putl(s, zrule->priority);
- stream_putl(s, zrule->unique);
+ stream_putc(s, r->family);
+ stream_putl(s, r->seq);
+ stream_putl(s, r->priority);
+ stream_putl(s, r->unique);
- zapi_encode_prefix(s, &(zrule->filter.src_ip),
- zrule->filter.src_ip.family);
- stream_putw(s, zrule->filter.src_port); /* src port */
- zapi_encode_prefix(s, &(zrule->filter.dst_ip),
- zrule->filter.src_ip.family);
- stream_putw(s, zrule->filter.dst_port); /* dst port */
- stream_putw(s, zrule->filter.fwmark); /* fwmark */
+ zapi_pbr_rule_filter_encode(s, &(r->filter));
+ zapi_pbr_rule_action_encode(s, &(r->action));
- stream_putl(s, zrule->action.table);
- stream_put(s, zrule->ifname, INTERFACE_NAMSIZ);
+ stream_put(s, r->ifname, IFNAMSIZ);
/* Put length at the first point of the stream. */
stream_putw_at(s, 0, stream_get_endp(s));
@@ -1642,6 +1868,29 @@ int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule)
return 0;
}
+bool zapi_pbr_rule_decode(struct stream *s, struct pbr_rule *r)
+{
+ /* NB caller has already read 4-byte rule count */
+
+ memset(r, 0, sizeof(*r));
+
+ STREAM_GETC(s, r->family);
+ STREAM_GETL(s, r->seq);
+ STREAM_GETL(s, r->priority);
+ STREAM_GETL(s, r->unique);
+
+ if (!zapi_pbr_rule_filter_decode(s, &(r->filter)))
+ goto stream_failure;
+ if (!zapi_pbr_rule_action_decode(s, &(r->action)))
+ goto stream_failure;
+
+ STREAM_GET(r->ifname, s, IFNAMSIZ);
+ return true;
+
+stream_failure:
+ return false;
+}
+
int zapi_tc_qdisc_encode(uint8_t cmd, struct stream *s, struct tc_qdisc *qdisc)
{
stream_reset(s);
@@ -1794,7 +2043,7 @@ bool zapi_rule_notify_decode(struct stream *s, uint32_t *seqno,
STREAM_GETL(s, seq);
STREAM_GETL(s, prio);
STREAM_GETL(s, uni);
- STREAM_GET(ifname, s, INTERFACE_NAMSIZ);
+ STREAM_GET(ifname, s, IFNAMSIZ);
if (zclient_debug)
zlog_debug("%s: %u %u %u %s", __func__, seq, prio, uni, ifname);
@@ -1902,8 +2151,8 @@ struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh)
nexthop_add_srv6_seg6local(n, znh->seg6local_action,
&znh->seg6local_ctx);
- if (!sid_zero(&znh->seg6_segs))
- nexthop_add_srv6_seg6(n, &znh->seg6_segs);
+ if (znh->seg_num && !sid_zero_ipv6(znh->seg6_segs))
+ nexthop_add_srv6_seg6(n, &znh->seg6_segs[0], znh->seg_num);
return n;
}
@@ -1963,10 +2212,14 @@ int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh,
sizeof(struct seg6local_context));
}
- if (!sid_zero(&nh->nh_srv6->seg6_segs)) {
+ if (nh->nh_srv6->seg6_segs && nh->nh_srv6->seg6_segs->num_segs &&
+ !sid_zero(nh->nh_srv6->seg6_segs)) {
SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_SEG6);
- memcpy(&znh->seg6_segs, &nh->nh_srv6->seg6_segs,
- sizeof(struct in6_addr));
+ znh->seg_num = nh->nh_srv6->seg6_segs->num_segs;
+ for (i = 0; i < nh->nh_srv6->seg6_segs->num_segs; i++)
+ memcpy(&znh->seg6_segs[i],
+ &nh->nh_srv6->seg6_segs->seg[i],
+ sizeof(struct in6_addr));
}
}
@@ -2025,8 +2278,8 @@ const char *zapi_nexthop2str(const struct zapi_nexthop *znh, char *buf,
/*
* Decode the nexthop-tracking update message
*/
-bool zapi_nexthop_update_decode(struct stream *s, struct prefix *match,
- struct zapi_route *nhr)
+static bool zapi_nexthop_update_decode(struct stream *s, struct prefix *match,
+ struct zapi_route *nhr)
{
uint32_t i;
@@ -2282,12 +2535,12 @@ static int zclient_vrf_delete(ZAPI_CALLBACK_ARGS)
static int zclient_interface_add(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
- char ifname_tmp[INTERFACE_NAMSIZ + 1] = {};
+ char ifname_tmp[IFNAMSIZ + 1] = {};
struct stream *s = zclient->ibuf;
struct vrf *vrf;
/* Read interface name. */
- STREAM_GET(ifname_tmp, s, INTERFACE_NAMSIZ);
+ STREAM_GET(ifname_tmp, s, IFNAMSIZ);
/* Lookup/create interface by name. */
vrf = vrf_lookup_by_id(vrf_id);
@@ -2318,10 +2571,10 @@ stream_failure:
struct interface *zebra_interface_state_read(struct stream *s, vrf_id_t vrf_id)
{
struct interface *ifp;
- char ifname_tmp[INTERFACE_NAMSIZ + 1] = {};
+ char ifname_tmp[IFNAMSIZ + 1] = {};
/* Read interface name. */
- STREAM_GET(ifname_tmp, s, INTERFACE_NAMSIZ);
+ STREAM_GET(ifname_tmp, s, IFNAMSIZ);
/* Lookup this by interface index. */
ifp = if_lookup_by_name(ifname_tmp, vrf_id);
@@ -2531,6 +2784,7 @@ static void zebra_interface_if_set_value(struct stream *s,
STREAM_GETC(s, ifp->ptm_status);
STREAM_GETL(s, ifp->metric);
STREAM_GETL(s, ifp->speed);
+ STREAM_GETL(s, ifp->txqlen);
STREAM_GETL(s, ifp->mtu);
STREAM_GETL(s, ifp->mtu6);
STREAM_GETL(s, ifp->bandwidth);
@@ -2806,36 +3060,6 @@ stream_failure:
return NULL;
}
-struct interface *zebra_interface_vrf_update_read(struct stream *s,
- vrf_id_t vrf_id,
- vrf_id_t *new_vrf_id)
-{
- char ifname[INTERFACE_NAMSIZ + 1] = {};
- struct interface *ifp;
- vrf_id_t new_id;
-
- /* Read interface name. */
- STREAM_GET(ifname, s, INTERFACE_NAMSIZ);
-
- /* Lookup interface. */
- ifp = if_lookup_by_name(ifname, vrf_id);
- if (ifp == NULL) {
- flog_err(EC_LIB_ZAPI_ENCODE,
- "INTERFACE_VRF_UPDATE: Cannot find IF %s in VRF %d",
- ifname, vrf_id);
- return NULL;
- }
-
- /* Fetch new VRF Id. */
- STREAM_GETL(s, new_id);
-
- *new_vrf_id = new_id;
- return ifp;
-
-stream_failure:
- return NULL;
-}
-
/* filter unwanted messages until the expected one arrives */
static int zclient_read_sync_response(struct zclient *zclient,
uint16_t expected_cmd)
@@ -3701,7 +3925,7 @@ enum zclient_send_status zebra_send_pw(struct zclient *zclient, int command,
stream_reset(s);
zclient_create_header(s, command, VRF_DEFAULT);
- stream_write(s, pw->ifname, INTERFACE_NAMSIZ);
+ stream_write(s, pw->ifname, IFNAMSIZ);
stream_putl(s, pw->ifindex);
/* Put type */
@@ -3748,7 +3972,7 @@ int zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS, struct zapi_pw_status *pw)
s = zclient->ibuf;
/* Get data. */
- stream_get(pw->ifname, s, INTERFACE_NAMSIZ);
+ stream_get(pw->ifname, s, IFNAMSIZ);
STREAM_GETL(s, pw->ifindex);
STREAM_GETL(s, pw->status);
@@ -3779,6 +4003,7 @@ static int zclient_capability_decode(ZAPI_CALLBACK_ARGS)
cap.mpls_enabled = !!mpls_enabled;
STREAM_GETL(s, cap.ecmp);
STREAM_GETC(s, cap.role);
+ STREAM_GETC(s, cap.v6_with_v4_nexthop);
if (zclient->zebra_capabilities)
(*zclient->zebra_capabilities)(&cap);
@@ -3824,6 +4049,53 @@ enum zclient_send_status zclient_send_mlag_data(struct zclient *client,
}
/*
+ * Init/header setup for opaque zapi messages
+ */
+enum zclient_send_status zapi_opaque_init(struct zclient *zclient,
+ uint32_t type, uint16_t flags)
+{
+ struct stream *s;
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT);
+
+ /* Send sub-type and flags */
+ stream_putl(s, type);
+ stream_putw(s, flags);
+
+ /* Source daemon identifiers */
+ stream_putc(s, zclient->redist_default);
+ stream_putw(s, zclient->instance);
+ stream_putl(s, zclient->session_id);
+
+ return ZCLIENT_SEND_SUCCESS;
+}
+
+/*
+ * Init, header setup for opaque unicast messages.
+ */
+enum zclient_send_status
+zapi_opaque_unicast_init(struct zclient *zclient, uint32_t type, uint16_t flags,
+ uint8_t proto, uint16_t instance, uint32_t session_id)
+{
+ struct stream *s;
+
+ s = zclient->obuf;
+
+ /* Common init */
+ zapi_opaque_init(zclient, type, flags | ZAPI_OPAQUE_FLAG_UNICAST);
+
+ /* Send destination client info */
+ stream_putc(s, proto);
+ stream_putw(s, instance);
+ stream_putl(s, session_id);
+
+ return ZCLIENT_SEND_SUCCESS;
+}
+
+/*
* Send an OPAQUE message, contents opaque to zebra. The message header
* is a message subtype.
*/
@@ -3840,16 +4112,12 @@ enum zclient_send_status zclient_send_opaque(struct zclient *zclient,
return ZCLIENT_SEND_FAILURE;
s = zclient->obuf;
- stream_reset(s);
-
- zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT);
- /* Send sub-type and flags */
- stream_putl(s, type);
- stream_putw(s, flags);
+ zapi_opaque_init(zclient, type, flags);
/* Send opaque data */
- stream_write(s, data, datasize);
+ if (datasize > 0)
+ stream_write(s, data, datasize);
/* Put length into the header at the start of the stream. */
stream_putw_at(s, 0, stream_get_endp(s));
@@ -3876,22 +4144,14 @@ zclient_send_opaque_unicast(struct zclient *zclient, uint32_t type,
return ZCLIENT_SEND_FAILURE;
s = zclient->obuf;
- stream_reset(s);
- zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT);
-
- /* Send sub-type and flags */
- SET_FLAG(flags, ZAPI_OPAQUE_FLAG_UNICAST);
- stream_putl(s, type);
- stream_putw(s, flags);
-
- /* Send destination client info */
- stream_putc(s, proto);
- stream_putw(s, instance);
- stream_putl(s, session_id);
+ /* Common init */
+ zapi_opaque_unicast_init(zclient, type, flags, proto, instance,
+ session_id);
/* Send opaque data */
- stream_write(s, data, datasize);
+ if (datasize > 0)
+ stream_write(s, data, datasize);
/* Put length into the header at the start of the stream. */
stream_putw_at(s, 0, stream_get_endp(s));
@@ -3910,11 +4170,16 @@ int zclient_opaque_decode(struct stream *s, struct zapi_opaque_msg *info)
STREAM_GETL(s, info->type);
STREAM_GETW(s, info->flags);
- /* Decode unicast client info if present */
+ /* Decode sending daemon info */
+ STREAM_GETC(s, info->src_proto);
+ STREAM_GETW(s, info->src_instance);
+ STREAM_GETL(s, info->src_session_id);
+
+ /* Decode unicast destination info, if present */
if (CHECK_FLAG(info->flags, ZAPI_OPAQUE_FLAG_UNICAST)) {
- STREAM_GETC(s, info->proto);
- STREAM_GETW(s, info->instance);
- STREAM_GETL(s, info->session_id);
+ STREAM_GETC(s, info->dest_proto);
+ STREAM_GETW(s, info->dest_instance);
+ STREAM_GETL(s, info->dest_session_id);
}
info->len = STREAM_READABLE(s);
@@ -4012,6 +4277,28 @@ stream_failure:
return -1;
}
+static int zclient_nexthop_update(ZAPI_CALLBACK_ARGS)
+{
+ struct vrf *vrf = vrf_lookup_by_id(vrf_id);
+ struct prefix match;
+ struct zapi_route route;
+
+ if (!vrf) {
+ zlog_warn("nexthop update for unknown VRF ID %u", vrf_id);
+ return 0;
+ }
+
+ if (!zapi_nexthop_update_decode(zclient->ibuf, &match, &route)) {
+ zlog_err("failed to decode nexthop update");
+ return -1;
+ }
+
+ if (zclient->nexthop_update)
+ zclient->nexthop_update(vrf, &match, &route);
+
+ return 0;
+}
+
static zclient_handler *const lib_handlers[] = {
/* fundamentals */
[ZEBRA_CAPABILITIES] = zclient_capability_decode,
@@ -4025,6 +4312,9 @@ static zclient_handler *const lib_handlers[] = {
[ZEBRA_INTERFACE_UP] = zclient_interface_up,
[ZEBRA_INTERFACE_DOWN] = zclient_interface_down,
+ /* NHT pre-decode */
+ [ZEBRA_NEXTHOP_UPDATE] = zclient_nexthop_update,
+
/* BFD */
[ZEBRA_BFD_DEST_REPLAY] = zclient_bfd_session_replay,
[ZEBRA_INTERFACE_BFD_DEST_UPDATE] = zclient_bfd_session_update,
@@ -4133,7 +4423,8 @@ static void zclient_read(struct event *thread)
zlog_debug("zclient %p command %s VRF %u", zclient,
zserv_command_string(command), vrf_id);
- if (command < array_size(lib_handlers) && lib_handlers[command])
+ if (!zclient->auxiliary && command < array_size(lib_handlers) &&
+ lib_handlers[command])
lib_handlers[command](command, zclient, length, vrf_id);
if (command < zclient->n_handlers && zclient->handlers[command])
zclient->handlers[command](command, zclient, length, vrf_id);
@@ -4168,15 +4459,15 @@ void zclient_redistribute(int command, struct zclient *zclient, afi_t afi,
} else {
if (command == ZEBRA_REDISTRIBUTE_ADD) {
- if (vrf_bitmap_check(zclient->redist[afi][type],
+ if (vrf_bitmap_check(&zclient->redist[afi][type],
vrf_id))
return;
- vrf_bitmap_set(zclient->redist[afi][type], vrf_id);
+ vrf_bitmap_set(&zclient->redist[afi][type], vrf_id);
} else {
- if (!vrf_bitmap_check(zclient->redist[afi][type],
+ if (!vrf_bitmap_check(&zclient->redist[afi][type],
vrf_id))
return;
- vrf_bitmap_unset(zclient->redist[afi][type], vrf_id);
+ vrf_bitmap_unset(&zclient->redist[afi][type], vrf_id);
}
}
@@ -4191,14 +4482,15 @@ void zclient_redistribute_default(int command, struct zclient *zclient,
{
if (command == ZEBRA_REDISTRIBUTE_DEFAULT_ADD) {
- if (vrf_bitmap_check(zclient->default_information[afi], vrf_id))
+ if (vrf_bitmap_check(&zclient->default_information[afi],
+ vrf_id))
return;
- vrf_bitmap_set(zclient->default_information[afi], vrf_id);
+ vrf_bitmap_set(&zclient->default_information[afi], vrf_id);
} else {
- if (!vrf_bitmap_check(zclient->default_information[afi],
+ if (!vrf_bitmap_check(&zclient->default_information[afi],
vrf_id))
return;
- vrf_bitmap_unset(zclient->default_information[afi], vrf_id);
+ vrf_bitmap_unset(&zclient->default_information[afi], vrf_id);
}
if (zclient->sock > 0)
@@ -4229,6 +4521,24 @@ static void zclient_event(enum zclient_event event, struct zclient *zclient)
}
}
+enum zclient_send_status zclient_interface_set_arp(struct zclient *client,
+ struct interface *ifp,
+ bool arp_enable)
+{
+ struct stream *s;
+
+ s = client->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_INTERFACE_SET_ARP, ifp->vrf->vrf_id);
+
+ stream_putl(s, ifp->ifindex);
+ stream_putc(s, arp_enable);
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+ return zclient_send_message(client);
+}
+
enum zclient_send_status zclient_interface_set_master(struct zclient *client,
struct interface *master,
struct interface *slave)
@@ -4418,7 +4728,7 @@ static int zclient_neigh_ip_read_entry(struct stream *s, struct ipaddr *add)
int zclient_neigh_ip_encode(struct stream *s, uint16_t cmd, union sockunion *in,
union sockunion *out, struct interface *ifp,
- int ndm_state)
+ int ndm_state, int ip_len)
{
int ret = 0;
@@ -4431,6 +4741,7 @@ int zclient_neigh_ip_encode(struct stream *s, uint16_t cmd, union sockunion *in,
sockunion_get_addrlen(out));
} else
stream_putc(s, AF_UNSPEC);
+ stream_putl(s, ip_len);
stream_putl(s, ifp->ifindex);
if (out)
stream_putl(s, ndm_state);
@@ -4448,6 +4759,7 @@ int zclient_neigh_ip_decode(struct stream *s, struct zapi_neigh_ip *api)
return -1;
zclient_neigh_ip_read_entry(s, &api->ip_out);
+ STREAM_GETL(s, api->ip_len);
STREAM_GETL(s, api->index);
STREAM_GETL(s, api->ndm_state);
return 0;
@@ -4472,3 +4784,145 @@ int zclient_send_zebra_gre_request(struct zclient *client,
zclient_send_message(client);
return 0;
}
+
+
+/*
+ * Opaque notification features
+ */
+
+/*
+ * Common encode helper for opaque notifications, both registration
+ * and async notification messages.
+ */
+static int opaque_notif_encode_common(struct stream *s, uint32_t msg_type,
+ bool request, bool reg, uint8_t proto,
+ uint16_t instance, uint32_t session_id)
+{
+ int ret = 0;
+ uint8_t val = 0;
+
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_OPAQUE_NOTIFY, VRF_DEFAULT);
+
+ /* Notification or request */
+ if (request)
+ val = 1;
+ stream_putc(s, val);
+
+ if (reg)
+ val = 1;
+ else
+ val = 0;
+ stream_putc(s, val);
+
+ stream_putl(s, msg_type);
+
+ stream_putc(s, proto);
+ stream_putw(s, instance);
+ stream_putl(s, session_id);
+
+ /* And capture message length */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return ret;
+}
+
+/*
+ * Encode a zapi opaque message type notification into buffer 's'
+ */
+int zclient_opaque_notif_encode(struct stream *s, uint32_t msg_type, bool reg,
+ uint8_t proto, uint16_t instance,
+ uint32_t session_id)
+{
+ return opaque_notif_encode_common(s, msg_type, false /* !request */,
+ reg, proto, instance, session_id);
+}
+
+/*
+ * Decode an incoming zapi opaque message type notification
+ */
+int zclient_opaque_notif_decode(struct stream *s,
+ struct zapi_opaque_notif_info *info)
+{
+ uint8_t val;
+
+ memset(info, 0, sizeof(*info));
+
+ STREAM_GETC(s, val); /* Registration or notification */
+ info->request = (val != 0);
+
+ STREAM_GETC(s, val);
+ info->reg = (val != 0);
+
+ STREAM_GETL(s, info->msg_type);
+
+ STREAM_GETC(s, info->proto);
+ STREAM_GETW(s, info->instance);
+ STREAM_GETL(s, info->session_id);
+
+ return 0;
+
+stream_failure:
+ return -1;
+}
+
+/*
+ * Encode and send a zapi opaque message type notification request to zebra
+ */
+enum zclient_send_status zclient_opaque_request_notify(struct zclient *zclient,
+ uint32_t msgtype)
+{
+ struct stream *s;
+
+ if (!zclient || zclient->sock < 0)
+ return ZCLIENT_SEND_FAILURE;
+
+ s = zclient->obuf;
+
+ opaque_notif_encode_common(s, msgtype, true /* request */,
+ true /* register */, zclient->redist_default,
+ zclient->instance, zclient->session_id);
+
+ return zclient_send_message(zclient);
+}
+
+/*
+ * Encode and send a request to drop notifications for an opaque message type.
+ */
+enum zclient_send_status zclient_opaque_drop_notify(struct zclient *zclient,
+ uint32_t msgtype)
+{
+ struct stream *s;
+
+ if (!zclient || zclient->sock < 0)
+ return ZCLIENT_SEND_FAILURE;
+
+ s = zclient->obuf;
+
+ opaque_notif_encode_common(s, msgtype, true /* req */,
+ false /* unreg */, zclient->redist_default,
+ zclient->instance, zclient->session_id);
+
+ return zclient_send_message(zclient);
+}
+
+void zclient_register_neigh(struct zclient *zclient, vrf_id_t vrf_id, afi_t afi,
+ bool reg)
+{
+ struct stream *s;
+
+ if (!zclient || zclient->sock < 0)
+ return;
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s,
+ reg ? ZEBRA_NEIGH_REGISTER
+ : ZEBRA_NEIGH_UNREGISTER,
+ vrf_id);
+ stream_putw(s, afi);
+ stream_putw_at(s, 0, stream_get_endp(s));
+ zclient_send_message(zclient);
+}
diff --git a/lib/zclient.h b/lib/zclient.h
index e43393fd70..1bf91064e2 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -87,7 +87,9 @@ enum zserv_client_capabilities {
extern struct sockaddr_storage zclient_addr;
extern socklen_t zclient_addr_len;
-/* Zebra message types. */
+/* Zebra message types. Please update the corresponding
+ * command_types array with any changes!
+ */
typedef enum {
ZEBRA_INTERFACE_ADD,
ZEBRA_INTERFACE_DELETE,
@@ -96,6 +98,7 @@ typedef enum {
ZEBRA_INTERFACE_UP,
ZEBRA_INTERFACE_DOWN,
ZEBRA_INTERFACE_SET_MASTER,
+ ZEBRA_INTERFACE_SET_ARP,
ZEBRA_INTERFACE_SET_PROTODOWN,
ZEBRA_ROUTE_ADD,
ZEBRA_ROUTE_DELETE,
@@ -125,7 +128,6 @@ typedef enum {
ZEBRA_VRF_ADD,
ZEBRA_VRF_DELETE,
ZEBRA_VRF_LABEL,
- ZEBRA_INTERFACE_VRF_UPDATE,
ZEBRA_BFD_CLIENT_REGISTER,
ZEBRA_BFD_CLIENT_DEREGISTER,
ZEBRA_INTERFACE_ENABLE_RADV,
@@ -215,11 +217,11 @@ typedef enum {
ZEBRA_NEIGH_DISCOVER,
ZEBRA_ROUTE_NOTIFY_REQUEST,
ZEBRA_CLIENT_CLOSE_NOTIFY,
- ZEBRA_NHRP_NEIGH_ADDED,
- ZEBRA_NHRP_NEIGH_REMOVED,
- ZEBRA_NHRP_NEIGH_GET,
- ZEBRA_NHRP_NEIGH_REGISTER,
- ZEBRA_NHRP_NEIGH_UNREGISTER,
+ ZEBRA_NEIGH_ADDED,
+ ZEBRA_NEIGH_REMOVED,
+ ZEBRA_NEIGH_GET,
+ ZEBRA_NEIGH_REGISTER,
+ ZEBRA_NEIGH_UNREGISTER,
ZEBRA_NEIGH_IP_ADD,
ZEBRA_NEIGH_IP_DEL,
ZEBRA_CONFIGURE_ARP,
@@ -232,7 +234,11 @@ typedef enum {
ZEBRA_TC_CLASS_DELETE,
ZEBRA_TC_FILTER_ADD,
ZEBRA_TC_FILTER_DELETE,
+ ZEBRA_OPAQUE_NOTIFY,
} zebra_message_types_t;
+/* Zebra message types. Please update the corresponding
+ * command_types array with any changes!
+ */
enum zebra_error_types {
ZEBRA_UNKNOWN_ERROR, /* Error of unknown type */
@@ -268,6 +274,7 @@ struct zclient_capabilities {
uint32_t ecmp;
bool mpls_enabled;
enum mlag_role role;
+ bool v6_with_v4_nexthop;
};
/* Graceful Restart Capabilities message */
@@ -287,6 +294,8 @@ struct zapi_cap {
typedef int (zclient_handler)(ZAPI_CALLBACK_ARGS);
/* clang-format on */
+struct zapi_route;
+
/* Structure for the zebra client. */
struct zclient {
/* The thread master we schedule ourselves on */
@@ -295,12 +304,14 @@ struct zclient {
/* Privileges to change socket values */
struct zebra_privs_t *privs;
- /* Do we care about failure events for route install? */
- bool receive_notify;
-
/* Is this a synchronous client? */
bool synchronous;
+ /* Auxiliary clients don't execute standard library handlers
+ * (which otherwise would duplicate VRF/interface add/delete/etc.
+ */
+ bool auxiliary;
+
/* BFD enabled with bfd_protocol_integration_init() */
bool bfd_integration;
@@ -342,6 +353,19 @@ struct zclient {
void (*zebra_connected)(struct zclient *);
void (*zebra_capabilities)(struct zclient_capabilities *cap);
+ /*
+ * match -> is the prefix that the calling daemon asked to be matched
+ * against.
+ * nhr->prefix -> is the actual prefix that was matched against in the
+ * rib itself.
+ *
+ * This distinction is made because a LPM can be made if there is a
+ * covering route. This way the upper level protocol can make a
+ * decision point about whether or not it wants to use the match or not.
+ */
+ void (*nexthop_update)(struct vrf *vrf, struct prefix *match,
+ struct zapi_route *nhr);
+
int (*handle_error)(enum zebra_error_types error);
/*
@@ -431,7 +455,8 @@ struct zapi_nexthop {
struct seg6local_context seg6local_ctx;
/* SRv6 Headend-behaviour */
- struct in6_addr seg6_segs;
+ int seg_num;
+ struct in6_addr seg6_segs[SRV6_MAX_SEGS];
};
/*
@@ -622,7 +647,7 @@ struct zapi_sr_policy {
};
struct zapi_pw {
- char ifname[INTERFACE_NAMSIZ];
+ char ifname[IFNAMSIZ];
ifindex_t ifindex;
int type;
int af;
@@ -635,7 +660,7 @@ struct zapi_pw {
};
struct zapi_pw_status {
- char ifname[INTERFACE_NAMSIZ];
+ char ifname[IFNAMSIZ];
ifindex_t ifindex;
uint32_t status;
};
@@ -812,11 +837,18 @@ extern char *zclient_evpn_dump_macip_flags(uint8_t flags, char *buf,
enum zebra_neigh_state { ZEBRA_NEIGH_INACTIVE = 0, ZEBRA_NEIGH_ACTIVE = 1 };
struct zclient_options {
- bool receive_notify;
bool synchronous;
+
+ /* auxiliary = don't call common lib/ handlers that manage bits.
+ * Those should only run once, on the "main" zclient, which this is
+ * not. (This is also set for synchronous clients.)
+ */
+ bool auxiliary;
};
-extern struct zclient_options zclient_options_default;
+extern const struct zclient_options zclient_options_default;
+extern const struct zclient_options zclient_options_sync;
+extern const struct zclient_options zclient_options_auxiliary;
/* link layer representation for GRE like interfaces
* ip_in is the underlay IP, ip_out is the tunnel dest
@@ -836,6 +868,7 @@ extern struct zclient_options zclient_options_default;
struct zapi_neigh_ip {
int cmd;
+ int ip_len;
struct ipaddr ip_in;
struct ipaddr ip_out;
ifindex_t index;
@@ -844,7 +877,7 @@ struct zapi_neigh_ip {
int zclient_neigh_ip_decode(struct stream *s, struct zapi_neigh_ip *api);
int zclient_neigh_ip_encode(struct stream *s, uint16_t cmd, union sockunion *in,
union sockunion *out, struct interface *ifp,
- int ndm_state);
+ int ndm_state, int ip_len);
/*
* We reserve the top 4 bits for l2-NHG, everything else
@@ -858,12 +891,12 @@ int zclient_neigh_ip_encode(struct stream *s, uint16_t cmd, union sockunion *in,
((uint32_t)250000000) /* Bottom 28 bits then rounded down */
#define ZEBRA_NHG_PROTO_SPACING (ZEBRA_NHG_PROTO_UPPER / ZEBRA_ROUTE_MAX)
#define ZEBRA_NHG_PROTO_LOWER \
- (ZEBRA_NHG_PROTO_SPACING * (ZEBRA_ROUTE_CONNECT + 1))
+ (ZEBRA_NHG_PROTO_SPACING * (ZEBRA_ROUTE_LOCAL + 1))
extern uint32_t zclient_get_nhg_start(uint32_t proto);
extern struct zclient *zclient_new(struct event_loop *m,
- struct zclient_options *opt,
+ const struct zclient_options *opt,
zclient_handler *const *handlers,
size_t n_handlers);
@@ -1004,6 +1037,9 @@ extern int zclient_read_header(struct stream *s, int sock, uint16_t *size,
*/
extern bool zapi_parse_header(struct stream *zmsg, struct zmsghdr *hdr);
+extern enum zclient_send_status zclient_interface_set_arp(struct zclient *client,
+ struct interface *ifp,
+ bool arp_enable);
extern enum zclient_send_status
zclient_interface_set_master(struct zclient *client, struct interface *master,
struct interface *slave);
@@ -1012,9 +1048,6 @@ extern struct connected *zebra_interface_address_read(int, struct stream *,
vrf_id_t);
extern struct nbr_connected *
zebra_interface_nbr_address_read(int, struct stream *, vrf_id_t);
-extern struct interface *zebra_interface_vrf_update_read(struct stream *s,
- vrf_id_t vrf_id,
- vrf_id_t *new_vrf_id);
extern int zebra_router_id_update_read(struct stream *s, struct prefix *rid);
extern struct interface *zebra_interface_link_params_read(struct stream *s,
@@ -1116,18 +1149,6 @@ int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh,
const struct nexthop *nh);
int zapi_backup_nexthop_from_nexthop(struct zapi_nexthop *znh,
const struct nexthop *nh);
-/*
- * match -> is the prefix that the calling daemon asked to be matched
- * against.
- * nhr->prefix -> is the actual prefix that was matched against in the
- * rib itself.
- *
- * This distinction is made because a LPM can be made if there is a
- * covering route. This way the upper level protocol can make a decision
- * point about whether or not it wants to use the match or not.
- */
-extern bool zapi_nexthop_update_decode(struct stream *s, struct prefix *match,
- struct zapi_route *nhr);
const char *zapi_nexthop2str(const struct zapi_nexthop *znh, char *buf,
int bufsize);
@@ -1150,6 +1171,15 @@ static inline void zapi_route_set_blackhole(struct zapi_route *api,
SET_FLAG(api->message, ZAPI_MESSAGE_NEXTHOP);
};
+static inline void zapi_route_set_nhg_id(struct zapi_route *api,
+ uint32_t *nhg_id)
+{
+ api->nexthop_num = 0;
+ api->nhgid = *nhg_id;
+ if (api->nhgid)
+ SET_FLAG(api->message, ZAPI_MESSAGE_NHG);
+};
+
extern enum zclient_send_status
zclient_send_mlag_register(struct zclient *client, uint32_t bit_map);
extern enum zclient_send_status
@@ -1176,16 +1206,33 @@ zclient_send_opaque_unicast(struct zclient *zclient, uint32_t type,
uint32_t session_id, const uint8_t *data,
size_t datasize);
+/* Init functions also provided for clients who want to encode their
+ * data inline into the zclient's stream buffer. Please use these instead
+ * of hand-encoding the header info, since that may change over time.
+ * Note that these will reset the zclient's outbound stream before encoding.
+ */
+enum zclient_send_status zapi_opaque_init(struct zclient *zclient,
+ uint32_t type, uint16_t flags);
+
+enum zclient_send_status
+zapi_opaque_unicast_init(struct zclient *zclient, uint32_t type, uint16_t flags,
+ uint8_t proto, uint16_t instance, uint32_t session_id);
+
/* Struct representing the decoded opaque header info */
struct zapi_opaque_msg {
uint32_t type; /* Subtype */
uint16_t len; /* len after zapi header and this info */
uint16_t flags;
- /* Client-specific info - *if* UNICAST flag is set */
- uint8_t proto;
- uint16_t instance;
- uint32_t session_id;
+ /* Sending client info */
+ uint8_t src_proto;
+ uint16_t src_instance;
+ uint32_t src_session_id;
+
+ /* Destination client info - *if* UNICAST flag is set */
+ uint8_t dest_proto;
+ uint16_t dest_instance;
+ uint32_t dest_session_id;
};
#define ZAPI_OPAQUE_FLAG_UNICAST 0x01
@@ -1201,6 +1248,34 @@ struct zapi_opaque_reg_info {
uint32_t session_id;
};
+/* Simple struct conveying information about opaque notifications.
+ * Daemons can request notifications about the status of registration for
+ * opaque message types. For example, a client daemon can request notification
+ * when a server registers to receive a certain message code. Or a server can
+ * request notification when a subscriber registers for its output.
+ */
+struct zapi_opaque_notif_info {
+ bool request; /* Request to register, or notification from zebra */
+ bool reg; /* Register or unregister */
+ uint32_t msg_type; /* Target message code */
+
+ /* For notif registration, zapi info for the client.
+ * For notifications, zapi info for the message's server/registrant.
+ * For notification that there is no server/registrant, not present.
+ */
+ uint8_t proto;
+ uint16_t instance;
+ uint32_t session_id;
+};
+
+/* The same ZAPI message is used for daemon->zebra requests, and for
+ * zebra->daemon notifications.
+ * Daemons send 'request' true, and 'reg' true or false.
+ * Zebra sends 'request' false, 'reg' set if the notification is a
+ * server/receiver registration for the message type, and false if the event
+ * is the end of registrations.
+ */
+
/* Decode incoming opaque */
int zclient_opaque_decode(struct stream *msg, struct zapi_opaque_msg *info);
@@ -1211,6 +1286,19 @@ enum zclient_send_status zclient_unregister_opaque(struct zclient *zclient,
int zapi_opaque_reg_decode(struct stream *msg,
struct zapi_opaque_reg_info *info);
+/* Opaque notification features */
+enum zclient_send_status zclient_opaque_request_notify(struct zclient *zclient,
+ uint32_t msgtype);
+enum zclient_send_status zclient_opaque_drop_notify(struct zclient *zclient,
+ uint32_t msgtype);
+
+/* Encode, decode an incoming zapi opaque notification */
+int zclient_opaque_notif_encode(struct stream *s, uint32_t msg_type,
+ bool reg /* register or unreg*/, uint8_t proto,
+ uint16_t instance, uint32_t session_id);
+int zclient_opaque_notif_decode(struct stream *s,
+ struct zapi_opaque_notif_info *info);
+
/*
* Registry of opaque message types. Please do not reuse an in-use
* type code; some daemons are likely relying on it.
@@ -1239,6 +1327,9 @@ enum zapi_opaque_registry {
*/
extern enum zclient_send_status zclient_send_hello(struct zclient *client);
+extern void zclient_register_neigh(struct zclient *zclient, vrf_id_t vrf_id,
+ afi_t afi, bool reg);
+
extern enum zclient_send_status
zclient_send_neigh_discovery_req(struct zclient *zclient,
const struct interface *ifp,
diff --git a/lib/zebra.h b/lib/zebra.h
index ecc87f58f1..15a54f6cdf 100644
--- a/lib/zebra.h
+++ b/lib/zebra.h
@@ -17,17 +17,9 @@
#include <stdlib.h>
#include <stddef.h>
#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <string.h>
-#include <pwd.h>
-#include <grp.h>
#ifdef HAVE_STROPTS_H
#include <stropts.h>
#endif /* HAVE_STROPTS_H */
-#include <sys/select.h>
-#include <sys/stat.h>
#include <sys/types.h>
#include <sys/param.h>
#ifdef HAVE_SYS_SYSCTL_H
@@ -37,22 +29,15 @@
#include <sys/sysctl.h>
#endif
#endif /* HAVE_SYS_SYSCTL_H */
-#include <sys/ioctl.h>
#ifdef HAVE_SYS_CONF_H
#include <sys/conf.h>
#endif /* HAVE_SYS_CONF_H */
#ifdef HAVE_SYS_KSYM_H
#include <sys/ksym.h>
#endif /* HAVE_SYS_KSYM_H */
-#include <syslog.h>
#include <sys/time.h>
#include <time.h>
-#include <sys/uio.h>
-#include <sys/utsname.h>
-#include <sys/resource.h>
-#include <limits.h>
#include <inttypes.h>
-#include <stdbool.h>
#ifdef HAVE_SYS_ENDIAN_H
#include <sys/endian.h>
#endif
@@ -63,11 +48,6 @@
/* misc include group */
#include <stdarg.h>
-#ifdef HAVE_LCAPS
-#include <sys/capability.h>
-#include <sys/prctl.h>
-#endif /* HAVE_LCAPS */
-
/* network include group */
#include <sys/socket.h>
@@ -76,10 +56,6 @@
#include <sys/sockio.h>
#endif /* HAVE_SYS_SOCKIO_H */
-#ifdef __APPLE__
-#define __APPLE_USE_RFC_3542
-#endif
-
#ifndef HAVE_LIBCRYPT
#ifdef HAVE_LIBCRYPTO
#include <openssl/des.h>
@@ -87,15 +63,9 @@
#endif
#endif
-#ifdef CRYPTO_OPENSSL
-#include <openssl/evp.h>
-#include <openssl/hmac.h>
-#endif
-
#include "openbsd-tree.h"
#include <netinet/in.h>
-#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
@@ -113,14 +83,9 @@
#include <net/if_var.h>
#endif /* HAVE_NET_IF_VAR_H */
-#include <net/route.h>
-
-#ifdef HAVE_NETLINK
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-#include <linux/filter.h>
-#else
+#ifndef HAVE_NETLINK
#define RT_TABLE_MAIN 0
+#define RT_TABLE_LOCAL RT_TABLE_MAIN
#endif /* HAVE_NETLINK */
#include <netdb.h>
@@ -146,37 +111,14 @@
#include <netinet6/in.h>
#endif /* HAVE_NETINET6_IN_H */
-
#ifdef HAVE_NETINET6_IP6_H
#include <netinet6/ip6.h>
#endif /* HAVE_NETINET6_IP6_H */
-#include <netinet/icmp6.h>
-
#ifdef HAVE_NETINET6_ND6_H
#include <netinet6/nd6.h>
#endif /* HAVE_NETINET6_ND6_H */
-/* Some systems do not define UINT32_MAX, etc.. from inttypes.h
- * e.g. this makes life easier for FBSD 4.11 users.
- */
-#ifndef INT16_MAX
-#define INT16_MAX (32767)
-#endif
-#ifndef INT32_MAX
-#define INT32_MAX (2147483647)
-#endif
-#ifndef UINT16_MAX
-#define UINT16_MAX (65535U)
-#endif
-#ifndef UINT32_MAX
-#define UINT32_MAX (4294967295U)
-#endif
-
-#ifdef HAVE_GLIBC_BACKTRACE
-#include <execinfo.h>
-#endif /* HAVE_GLIBC_BACKTRACE */
-
/* Local includes: */
#if !defined(__GNUC__)
#define __attribute__(x)
@@ -210,26 +152,6 @@ size_t strlcpy(char *__restrict dest,
void explicit_bzero(void *buf, size_t len);
#endif
-#if !defined(HAVE_STRUCT_MMSGHDR_MSG_HDR) || !defined(HAVE_SENDMMSG)
-/* avoid conflicts in case we have partial support */
-#define mmsghdr frr_mmsghdr
-#define sendmmsg frr_sendmmsg
-
-struct mmsghdr {
- struct msghdr msg_hdr;
- unsigned int msg_len;
-};
-
-/* just go 1 at a time here, the loop this is used in will handle the rest */
-static inline int sendmmsg(int fd, struct mmsghdr *mmh, unsigned int len,
- int flags)
-{
- int rv = sendmsg(fd, &mmh->msg_hdr, 0);
-
- return rv > 0 ? 1 : rv;
-}
-#endif
-
/*
* RFC 3542 defines several macros for using struct cmsghdr.
* Here, we define those that are not present
@@ -283,10 +205,9 @@ struct in_pktinfo {
* OpenBSD: network byte order, apart from older versions which are as per
* *BSD
*/
-#if defined(__NetBSD__) \
- || (defined(__FreeBSD__) && (__FreeBSD_version < 1100030)) \
- || (defined(__OpenBSD__) && (OpenBSD < 200311)) \
- || (defined(__APPLE__))
+#if defined(__NetBSD__) || \
+ (defined(__FreeBSD__) && (__FreeBSD_version < 1100030)) || \
+ (defined(__OpenBSD__) && (OpenBSD < 200311))
#define HAVE_IP_HDRINCL_BSD_ORDER
#endif
@@ -300,15 +221,6 @@ struct in_pktinfo {
#define IN6_ARE_ADDR_EQUAL IN6_IS_ADDR_EQUAL
#endif /* IN6_ARE_ADDR_EQUAL */
-/* default zebra TCP port for zclient */
-#define ZEBRA_PORT 2600
-
-/*
- * The compiler.h header is used for anyone using the CPP_NOTICE
- * since this is universally needed, let's add it to zebra.h
- */
-#include "compiler.h"
-
/* Zebra route's types are defined in route_types.h */
#include "lib/route_types.h"
@@ -358,27 +270,6 @@ typedef enum {
for (afi = AFI_IP; afi < AFI_MAX; afi++) \
for (safi = SAFI_UNICAST; safi <= SAFI_MPLS_VPN; safi++)
-/* Default Administrative Distance of each protocol. */
-#define ZEBRA_KERNEL_DISTANCE_DEFAULT 0
-#define ZEBRA_CONNECT_DISTANCE_DEFAULT 0
-#define ZEBRA_STATIC_DISTANCE_DEFAULT 1
-#define ZEBRA_RIP_DISTANCE_DEFAULT 120
-#define ZEBRA_RIPNG_DISTANCE_DEFAULT 120
-#define ZEBRA_OSPF_DISTANCE_DEFAULT 110
-#define ZEBRA_OSPF6_DISTANCE_DEFAULT 110
-#define ZEBRA_ISIS_DISTANCE_DEFAULT 115
-#define ZEBRA_IBGP_DISTANCE_DEFAULT 200
-#define ZEBRA_EBGP_DISTANCE_DEFAULT 20
-#define ZEBRA_TABLE_DISTANCE_DEFAULT 15
-#define ZEBRA_EIGRP_DISTANCE_DEFAULT 90
-#define ZEBRA_NHRP_DISTANCE_DEFAULT 10
-#define ZEBRA_LDP_DISTANCE_DEFAULT 150
-#define ZEBRA_BABEL_DISTANCE_DEFAULT 100
-#define ZEBRA_SHARP_DISTANCE_DEFAULT 150
-#define ZEBRA_PBR_DISTANCE_DEFAULT 200
-#define ZEBRA_OPENFABRIC_DISTANCE_DEFAULT 115
-#define ZEBRA_MAX_DISTANCE_DEFAULT 255
-
/* Flag manipulation macros. */
#define CHECK_FLAG(V,F) ((V) & (F))
#define SET_FLAG(V,F) (V) |= (F)
diff --git a/lib/zlog.c b/lib/zlog.c
index 309a955fa9..ffca572609 100644
--- a/lib/zlog.c
+++ b/lib/zlog.c
@@ -4,6 +4,12 @@
*/
#include "zebra.h"
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#ifdef HAVE_GLIBC_BACKTRACE
+#include <execinfo.h>
+#endif /* HAVE_GLIBC_BACKTRACE */
#include <unistd.h>
#include <sys/time.h>
@@ -32,9 +38,6 @@
#ifdef __DragonFly__
#include <sys/lwp.h>
#endif
-#ifdef __APPLE__
-#include <mach/mach_traps.h>
-#endif
#ifdef HAVE_LIBUNWIND
#define UNW_LOCAL_ONLY
@@ -47,6 +50,7 @@
#include "printfrr.h"
#include "frrcu.h"
#include "zlog.h"
+#include "zlog_live.h"
#include "libfrr_trace.h"
#include "frrevent.h"
@@ -81,7 +85,7 @@ static struct zlog_targets_head zlog_targets;
/* Global setting for buffered vs immediate output. The default is
* per-pthread buffering.
*/
-static bool default_immediate;
+static bool zlog_default_immediate;
/* cf. zlog.h for additional comments on this struct.
*
@@ -106,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
@@ -354,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
@@ -442,7 +459,7 @@ static void vzlog_tls(struct zlog_tls *zlog_tls, const struct xref_logmsg *xref,
struct zlog_msg *msg;
char *buf;
bool ignoremsg = true;
- bool immediate = default_immediate;
+ bool immediate = zlog_default_immediate;
/* avoid further processing cost if no target wants this message */
rcu_read_lock();
@@ -504,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);
@@ -963,7 +1063,12 @@ struct zlog_target *zlog_target_replace(struct zlog_target *oldzt,
*/
void zlog_set_immediate(bool set_p)
{
- default_immediate = set_p;
+ zlog_default_immediate = set_p;
+}
+
+bool zlog_get_immediate_mode(void)
+{
+ return zlog_default_immediate;
}
/* common init */
diff --git a/lib/zlog.h b/lib/zlog.h
index a207b29a3b..9d93979957 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)
@@ -276,6 +279,7 @@ extern void zlog_tls_buffer_fini(void);
/* Enable or disable 'immediate' output - default is to buffer messages. */
extern void zlog_set_immediate(bool set_p);
+bool zlog_get_immediate_mode(void);
extern const char *zlog_priority_str(int priority);
diff --git a/lib/zlog_5424.c b/lib/zlog_5424.c
index c15bdece29..4c60d4b405 100644
--- a/lib/zlog_5424.c
+++ b/lib/zlog_5424.c
@@ -13,6 +13,9 @@
*/
#include "zebra.h"
+#include <fcntl.h>
+
+#include "frrsendmmsg.h"
#include "zlog_5424.h"
@@ -877,10 +880,15 @@ static int zlog_5424_open(struct zlog_cfg_5424 *zcf, int sock_type)
switch (zcf->dst) {
case ZLOG_5424_DST_NONE:
- break;
+ return -1;
case ZLOG_5424_DST_FD:
fd = dup(zcf->fd);
+ if (fd < 0) {
+ flog_err_sys(EC_LIB_SYSTEM_CALL,
+ "failed to dup() log file descriptor: %m (FD limit too low?)");
+ return -1;
+ }
optlen = sizeof(sock_type);
if (!getsockopt(fd, SOL_SOCKET, SO_TYPE, &sock_type, &optlen)) {
@@ -891,7 +899,7 @@ static int zlog_5424_open(struct zlog_cfg_5424 *zcf, int sock_type)
case ZLOG_5424_DST_FIFO:
if (!zcf->filename)
- break;
+ return -1;
if (!zcf->file_nocreate) {
frr_with_privs (lib_privs) {
@@ -904,15 +912,15 @@ static int zlog_5424_open(struct zlog_cfg_5424 *zcf, int sock_type)
if (err == 0)
do_chown = true;
else if (errno != EEXIST)
- break;
+ return -1;
}
flags = O_NONBLOCK;
- /* fallthru */
+ fallthrough;
case ZLOG_5424_DST_FILE:
if (!zcf->filename)
- break;
+ return -1;
frr_with_privs (lib_privs) {
fd = open(zcf->filename, flags | O_WRONLY | O_APPEND |
@@ -924,7 +932,7 @@ static int zlog_5424_open(struct zlog_cfg_5424 *zcf, int sock_type)
flog_err_sys(EC_LIB_SYSTEM_CALL,
"could not open log file %pSE: %m",
zcf->filename);
- break;
+ return -1;
}
frr_with_privs (lib_privs) {
@@ -952,11 +960,11 @@ static int zlog_5424_open(struct zlog_cfg_5424 *zcf, int sock_type)
flog_err_sys(EC_LIB_SYSTEM_CALL,
"could not open or create log file %pSE: %m",
zcf->filename);
- break;
+ return -1;
case ZLOG_5424_DST_UNIX:
if (!zcf->filename)
- break;
+ return -1;
memset(&sa, 0, sizeof(sa));
sa.sun_family = AF_UNIX;
@@ -988,6 +996,7 @@ static int zlog_5424_open(struct zlog_cfg_5424 *zcf, int sock_type)
"could not connect to log unix path %pSE: %m",
zcf->filename);
need_reconnect = true;
+ /* no return -1 here, trigger retry code below */
} else {
/* datagram sockets are connectionless, restarting
* the receiver may lose some packets but will resume
diff --git a/lib/zlog_live.c b/lib/zlog_live.c
index 4d3c3508bf..1b7696c893 100644
--- a/lib/zlog_live.c
+++ b/lib/zlog_live.c
@@ -5,6 +5,8 @@
#include "zebra.h"
+#include "frrsendmmsg.h"
+
#include "zlog_live.h"
#include "memory.h"
diff --git a/lib/zlog_recirculate.c b/lib/zlog_recirculate.c
new file mode 100644
index 0000000000..abc73eeed0
--- /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 0000000000..a2ddb4e172
--- /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
diff --git a/lib/zlog_targets.c b/lib/zlog_targets.c
index b0f7571492..bbd228f28c 100644
--- a/lib/zlog_targets.c
+++ b/lib/zlog_targets.c
@@ -5,6 +5,7 @@
#include "zebra.h"
+#include <fcntl.h>
#include <sys/un.h>
#include <syslog.h>