summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_attr.c9
-rw-r--r--bgpd/bgp_bmp.c14
-rw-r--r--bgpd/bgp_fsm.c17
-rw-r--r--bgpd/bgp_routemap.c22
-rw-r--r--bgpd/bgp_vty.c22
-rw-r--r--bgpd/bgp_zebra.c9
-rw-r--r--bgpd/bgpd.c6
-rw-r--r--configure.ac13
-rw-r--r--debian/control2
-rw-r--r--doc/user/isisd.rst17
-rw-r--r--fpm/fpm_pb.c5
-rw-r--r--isisd/isis_adjacency.c4
-rw-r--r--isisd/isis_lsp.c13
-rw-r--r--isisd/isis_spf.c129
-rw-r--r--isisd/isis_te.c51
-rw-r--r--isisd/isisd.c305
-rw-r--r--lib/elf_py.c9
-rw-r--r--lib/frr_zmq.c2
-rw-r--r--lib/if.c19
-rw-r--r--lib/mgmt.proto1
-rw-r--r--lib/mgmt_be_client.c143
-rw-r--r--lib/mgmt_be_client.h2
-rw-r--r--lib/mgmt_fe_client.c45
-rw-r--r--lib/mgmt_fe_client.h35
-rw-r--r--lib/mgmt_msg_native.c2
-rw-r--r--lib/mgmt_msg_native.h45
-rw-r--r--lib/northbound.c27
-rw-r--r--lib/northbound.h10
-rw-r--r--lib/northbound_cli.c75
-rw-r--r--lib/northbound_cli.h34
-rw-r--r--lib/northbound_grpc.cpp61
-rw-r--r--lib/northbound_oper.c5
-rw-r--r--lib/northbound_sysrepo.c65
-rw-r--r--lib/vty.c64
-rw-r--r--lib/vty.h6
-rw-r--r--lib/yang.c121
-rw-r--r--lib/yang.h34
-rw-r--r--mgmtd/mgmt_be_adapter.c141
-rw-r--r--mgmtd/mgmt_be_adapter.h12
-rw-r--r--mgmtd/mgmt_be_nb.c6
-rw-r--r--mgmtd/mgmt_fe_adapter.c151
-rw-r--r--mgmtd/mgmt_fe_adapter.h20
-rw-r--r--mgmtd/mgmt_memory.c2
-rw-r--r--mgmtd/mgmt_memory.h2
-rw-r--r--mgmtd/mgmt_testc.c34
-rw-r--r--mgmtd/mgmt_txn.c238
-rw-r--r--mgmtd/mgmt_txn.h37
-rw-r--r--mgmtd/mgmt_vty.c16
-rw-r--r--mgmtd/subdir.am1
-rw-r--r--ospf6d/ospf6_abr.c11
-rw-r--r--ospf6d/ospf6_abr.h5
-rw-r--r--ospf6d/ospf6_asbr.c85
-rw-r--r--ospf6d/ospf6_asbr.h11
-rw-r--r--ospf6d/ospf6_route.h3
-rw-r--r--ospf6d/ospf6_zebra.c2
-rw-r--r--ospfd/ospf_vty.c2
-rw-r--r--python/clippy/__init__.py1
-rw-r--r--python/clippy/elf.py11
-rw-r--r--python/xrelfo.py15
-rw-r--r--qpb/qpb.c5
-rw-r--r--ripd/rip_cli.c19
-rw-r--r--ripd/rip_nb_rpcs.c9
-rw-r--r--ripd/ripd.c33
-rw-r--r--ripd/subdir.am1
-rw-r--r--ripngd/ripng_cli.c19
-rw-r--r--ripngd/ripng_nb_rpcs.c9
-rw-r--r--ripngd/ripngd.c33
-rw-r--r--ripngd/subdir.am1
-rw-r--r--tests/lib/northbound/test_oper_data.c47
-rw-r--r--tests/lib/northbound/test_oper_data.in1
-rw-r--r--tests/lib/northbound/test_oper_data.refout2
-rw-r--r--tests/topotests/bgp_path_attribute_discard/peer1/exabgp.cfg29
-rw-r--r--tests/topotests/bgp_path_attribute_discard/r1/bgpd.conf6
-rw-r--r--tests/topotests/bgp_path_attribute_discard/r1/frr.conf9
-rw-r--r--tests/topotests/bgp_path_attribute_discard/r1/zebra.conf4
-rw-r--r--tests/topotests/bgp_path_attribute_discard/r2/frr.conf10
-rw-r--r--tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py37
-rw-r--r--tests/topotests/mgmt_rpc/r1/frr.conf0
-rw-r--r--tests/topotests/mgmt_rpc/test_rpc.py74
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r5/ospf6d.conf22
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r6/ospf6d.conf15
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r7/ospf6d.conf15
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r7/zebra.conf3
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r8/ospf6d.conf13
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r8/zebra.conf3
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r9/ospf6d.conf11
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r9/zebra.conf5
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/test_ospf6_ecmp_inter_area.py117
-rw-r--r--tools/gcc-plugins/frr-format.c6
-rw-r--r--yang/frr-test-module.yang17
-rw-r--r--zebra/fpm_listener.c2
-rw-r--r--zebra/zebra_evpn_mh.c37
-rw-r--r--zebra/zebra_evpn_mh.h3
-rw-r--r--zebra/zebra_l2.c2
-rw-r--r--zebra/zebra_nb_rpcs.c50
-rw-r--r--zebra/zebra_vty.c53
-rw-r--r--zebra/zebra_vxlan_if.c35
97 files changed, 2212 insertions, 794 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 913634bf59..71f02a7f83 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -1526,6 +1526,7 @@ static bool bgp_attr_flag_invalid(struct bgp_attr_parser_args *args)
uint8_t mask = BGP_ATTR_FLAG_EXTLEN;
const uint8_t flags = args->flags;
const uint8_t attr_code = args->type;
+ struct peer *peer = args->peer;
/* there may be attributes we don't know about */
if (attr_code > attr_flags_values_max)
@@ -1533,6 +1534,14 @@ static bool bgp_attr_flag_invalid(struct bgp_attr_parser_args *args)
if (attr_flags_values[attr_code] == 0)
return false;
+ /* If `neighbor X path-attribute <discard|treat-as-withdraw>` is
+ * configured, then ignore checking optional, trasitive flags.
+ * The attribute/route will be discarded/withdrawned later instead
+ * of dropping the session.
+ */
+ if (peer->discard_attrs[attr_code] || peer->withdraw_attrs[attr_code])
+ return false;
+
/* RFC4271, "For well-known attributes, the Transitive bit MUST be set
* to
* 1."
diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c
index 14066ae29d..2f3be0bc9d 100644
--- a/bgpd/bgp_bmp.c
+++ b/bgpd/bgp_bmp.c
@@ -391,11 +391,11 @@ static int bmp_send_initiation(struct bmp *bmp)
bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_INITIATION);
-#define BMP_INFO_TYPE_SYSDESCR 1
-#define BMP_INFO_TYPE_SYSNAME 2
- bmp_put_info_tlv(s, BMP_INFO_TYPE_SYSDESCR,
- FRR_FULL_NAME " " FRR_VER_SHORT);
- bmp_put_info_tlv(s, BMP_INFO_TYPE_SYSNAME, cmd_hostname_get());
+#define BMP_INIT_INFO_TYPE_SYSDESCR 1
+#define BMP_INIT_INFO_TYPE_SYSNAME 2
+ bmp_put_info_tlv(s, BMP_INIT_INFO_TYPE_SYSDESCR,
+ FRR_FULL_NAME " " FRR_VER_SHORT);
+ bmp_put_info_tlv(s, BMP_INIT_INFO_TYPE_SYSNAME, cmd_hostname_get());
len = stream_get_endp(s);
stream_putl_at(s, BMP_LENGTH_POS, len); /* message length is set. */
@@ -438,6 +438,7 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down)
monotime_to_realtime(&uptime, &uptime_real);
#define BGP_BMP_MAX_PACKET_SIZE 1024
+#define BMP_PEERUP_INFO_TYPE_STRING 0
s = stream_new(BGP_MAX_PACKET_SIZE);
if (peer_established(peer->connection) && !down) {
@@ -493,7 +494,8 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down)
}
if (peer->desc)
- bmp_put_info_tlv(s, 0, peer->desc);
+ bmp_put_info_tlv(s, BMP_PEERUP_INFO_TYPE_STRING,
+ peer->desc);
} else {
uint8_t type;
size_t type_pos;
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index 6ec26d35f5..f10705ef21 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -2919,19 +2919,22 @@ int bgp_neighbor_graceful_restart(struct peer *peer,
peer_old_state = bgp_peer_gr_mode_get(peer);
- if (peer_old_state == PEER_INVALID) {
- zlog_debug("[BGP_GR] peer_old_state == Invalid state !");
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("%s [BGP_GR] peer_old_state: %d", __func__,
+ peer_old_state);
+
+ if (peer_old_state == PEER_INVALID)
return BGP_ERR_GR_OPERATION_FAILED;
- }
peer_state = peer->PEER_GR_FSM[peer_old_state][peer_gr_cmd];
peer_new_state = peer_state.next_state;
- if (peer_new_state == PEER_INVALID) {
- zlog_debug(
- "[BGP_GR] Invalid bgp graceful restart command used !");
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("%s [BGP_GR] peer_new_state: %d", __func__,
+ peer_new_state);
+
+ if (peer_new_state == PEER_INVALID)
return BGP_ERR_GR_INVALID_CMD;
- }
if (peer_new_state != peer_old_state) {
result = peer_state.action_fun(peer, peer_old_state,
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 4875697aa1..df5c2557bd 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -5897,10 +5897,11 @@ DEFUN_YANG (set_table_id,
DEFUN_YANG (no_set_table_id,
no_set_table_id_cmd,
- "no set table",
+ "no set table [(1-4294967295)]",
NO_STR
SET_STR
- "export route to non-main kernel table\n")
+ "export route to non-main kernel table\n"
+ "Kernel routing table id\n")
{
const char *xpath = "./set-action[action='frr-bgp-route-map:table']";
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
@@ -6288,13 +6289,12 @@ DEFPY_YANG(
}
DEFUN_YANG (no_set_aspath_prepend,
- no_set_aspath_prepend_cmd,
- "no set as-path prepend [ASNUM] [last-as [(1-10)]]",
+ no_set_aspath_prepend_last_as_cmd,
+ "no set as-path prepend [last-as [(1-10)]]",
NO_STR
SET_STR
"Transform BGP AS_PATH attribute\n"
"Prepend to the as-path\n"
- AS_STR
"Use the peers AS-number\n"
"Number of times to insert\n")
{
@@ -6305,6 +6305,15 @@ DEFUN_YANG (no_set_aspath_prepend,
return nb_cli_apply_changes(vty, NULL);
}
+ALIAS_YANG (no_set_aspath_prepend,
+ no_set_aspath_prepend_as_cmd,
+ "no set as-path prepend ASNUM...",
+ NO_STR
+ SET_STR
+ "Transform BGP AS_PATH attribute\n"
+ "Prepend to the as-path\n"
+ AS_STR)
+
DEFUN_YANG (set_aspath_exclude,
set_aspath_exclude_cmd,
"set as-path exclude ASNUM...",
@@ -7981,7 +7990,8 @@ void bgp_route_map_init(void)
install_element(RMAP_NODE, &set_aspath_exclude_access_list_cmd);
install_element(RMAP_NODE, &set_aspath_replace_asn_cmd);
install_element(RMAP_NODE, &set_aspath_replace_access_list_cmd);
- install_element(RMAP_NODE, &no_set_aspath_prepend_cmd);
+ install_element(RMAP_NODE, &no_set_aspath_prepend_last_as_cmd);
+ install_element(RMAP_NODE, &no_set_aspath_prepend_as_cmd);
install_element(RMAP_NODE, &no_set_aspath_exclude_cmd);
install_element(RMAP_NODE, &no_set_aspath_exclude_all_cmd);
install_element(RMAP_NODE, &no_set_aspath_exclude_access_list_cmd);
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 5aedd6b828..d5f12897ea 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -3347,7 +3347,7 @@ DEFUN (bgp_neighbor_graceful_restart_set,
{
int idx_peer = 1;
struct peer *peer;
- int ret = BGP_GR_FAILURE;
+ int result = BGP_GR_FAILURE, ret = BGP_GR_SUCCESS;
VTY_BGP_GR_DEFINE_LOOP_VARIABLE;
@@ -3359,8 +3359,8 @@ DEFUN (bgp_neighbor_graceful_restart_set,
if (!peer)
return CMD_WARNING_CONFIG_FAILED;
- ret = bgp_neighbor_graceful_restart(peer, PEER_GR_CMD);
- if (ret == BGP_GR_SUCCESS) {
+ result = bgp_neighbor_graceful_restart(peer, PEER_GR_CMD);
+ if (result == BGP_GR_SUCCESS) {
VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
vty_out(vty,
@@ -3371,7 +3371,11 @@ DEFUN (bgp_neighbor_graceful_restart_set,
zlog_debug(
"[BGP_GR] bgp_neighbor_graceful_restart_set_cmd : END ");
- return bgp_vty_return(vty, ret);
+ if (ret != BGP_GR_SUCCESS)
+ vty_out(vty,
+ "As part of configuring graceful-restart, capability send to zebra failed\n");
+
+ return bgp_vty_return(vty, result);
}
DEFUN (no_bgp_neighbor_graceful_restart,
@@ -3384,7 +3388,7 @@ DEFUN (no_bgp_neighbor_graceful_restart,
)
{
int idx_peer = 2;
- int ret = BGP_GR_FAILURE;
+ int result = BGP_GR_FAILURE, ret = BGP_GR_SUCCESS;
struct peer *peer;
VTY_BGP_GR_DEFINE_LOOP_VARIABLE;
@@ -3397,7 +3401,7 @@ DEFUN (no_bgp_neighbor_graceful_restart,
zlog_debug(
"[BGP_GR] no_bgp_neighbor_graceful_restart_set_cmd : START ");
- ret = bgp_neighbor_graceful_restart(peer, NO_PEER_GR_CMD);
+ result = bgp_neighbor_graceful_restart(peer, NO_PEER_GR_CMD);
if (ret == BGP_GR_SUCCESS) {
VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
@@ -3409,7 +3413,11 @@ DEFUN (no_bgp_neighbor_graceful_restart,
zlog_debug(
"[BGP_GR] no_bgp_neighbor_graceful_restart_set_cmd : END ");
- return bgp_vty_return(vty, ret);
+ if (ret != BGP_GR_SUCCESS)
+ vty_out(vty,
+ "As part of configuring graceful-restart, capability send to zebra failed\n");
+
+ return bgp_vty_return(vty, result);
}
DEFUN (bgp_neighbor_graceful_restart_helper_set,
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index 15ed98933e..52c75a3dc2 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -3385,12 +3385,12 @@ static int bgp_zebra_process_srv6_locator_add(ZAPI_CALLBACK_ARGS)
struct bgp *bgp = bgp_get_default();
const char *loc_name = bgp->srv6_locator_name;
- if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0)
- return -1;
-
if (!bgp || !bgp->srv6_enabled)
return 0;
+ if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0)
+ return -1;
+
if (bgp_zebra_srv6_manager_get_locator_chunk(loc_name) < 0)
return -1;
@@ -3408,6 +3408,9 @@ static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS)
struct in6_addr *tovpn_sid;
struct prefix_ipv6 tmp_prefi;
+ if (!bgp)
+ return 0;
+
if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0)
return -1;
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index ff17dc9f5e..cd083d889a 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -1446,11 +1446,11 @@ int bgp_peer_gr_init(struct peer *peer)
{
/* PEER_GLOBAL_INHERIT Mode */
/* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */
- { PEER_GR, bgp_peer_gr_action }, { PEER_INVALID, NULL },
+ { PEER_GR, bgp_peer_gr_action }, { PEER_GLOBAL_INHERIT, NULL },
/* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */
- { PEER_DISABLE, bgp_peer_gr_action}, { PEER_INVALID, NULL },
+ { PEER_DISABLE, bgp_peer_gr_action }, { PEER_GLOBAL_INHERIT, NULL },
/* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */
- { PEER_HELPER, bgp_peer_gr_action }, { PEER_INVALID, NULL }
+ { PEER_HELPER, bgp_peer_gr_action }, { PEER_GLOBAL_INHERIT, NULL }
}
};
memcpy(&peer->PEER_GR_FSM, local_Peer_GR_FSM,
diff --git a/configure.ac b/configure.ac
index 9f964a2f3e..cad8ea12c0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -275,7 +275,16 @@ AC_DEFUN([AC_C_FLAG], [{
AC_CACHE_CHECK([[whether $CC supports $1]], cachename, [
AC_LANG_PUSH([C])
ac_c_flag_save="$CFLAGS"
- CFLAGS="$CFLAGS $1"
+ dnl GCC ignores unknown -Wno-whatever flags, but errors on -Wwhatever
+ dnl except when it ignores them it prints:
+ dnl cc1: note: unrecognized command-line option ‘-Wno-whatever’ may have been intended to silence earlier diagnostics
+ dnl which is annoying as hell. So check for the positive flag instead.
+ flag_add="$1"
+ if test "$flag_add" != "${flag_add#-Wno-}"; then
+ CFLAGS="$CFLAGS -W${flag_add#-Wno-}"
+ else
+ CFLAGS="$CFLAGS $flag_add"
+ fi
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM([[$4]])],
[
@@ -1096,6 +1105,8 @@ dnl -------------------------
AC_CHECK_HEADERS([stropts.h sys/ksym.h \
linux/version.h asm/types.h endian.h sys/endian.h])
+AC_CHECK_LIB([atomic], [main], [LIBS="$LIBS -latomic"], [], [])
+
ac_stdatomic_ok=false
AC_DEFINE([FRR_AUTOCONF_ATOMIC], [1], [did autoconf checks for atomic funcs])
AC_CHECK_HEADER([stdatomic.h],[
diff --git a/debian/control b/debian/control
index e6d65133d3..12b80a77f3 100644
--- a/debian/control
+++ b/debian/control
@@ -23,7 +23,7 @@ Build-Depends: bison,
librtr-dev (>= 0.8.0~) <!pkg.frr.nortrlib>,
libsnmp-dev,
libssh-dev <!pkg.frr.nortrlib>,
- libyang2-dev (>= 2.1.128),
+ libyang2-dev (>= 2.1.128) | libyang-dev ( >= 3.0.3),
lsb-base,
pkg-config,
protobuf-c-compiler,
diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst
index 135d94004a..7412611869 100644
--- a/doc/user/isisd.rst
+++ b/doc/user/isisd.rst
@@ -462,10 +462,14 @@ To do so, it defines a set of Flex-Algo Definitions (FAD) which
have the following characteristics:
- a numeric identifier (ID) between 128 and 255 inclusive
+
- a set of constraints (basically, include or exclude a certain given set of
links, designated by a admin-group)
+
- the calculation type (only the `Shortest-Path-First` is currently supported)
+
- the metric type (only the IGP inherited metric type is currently supported)
+
- some additional flags (not supported for the moment).
A subset of routers advertises the Flex-Algo Definitions (FAD) to the other
@@ -475,13 +479,18 @@ rules:
- If a locally configured FAD is not advertised to the area, the router does not
participate in the particular flex algorithm.
+
- If a given flex algorithm is running, the participation in this particular
flex algorithm stops when its advertisements are over.
+
- A router includes its own FAD in the election process if and only if it is
advertised to the other routers.
+
- If only one router advertises the FAD, the FAD is elected.
+
- If several FADs are advertised with different priorities, the one with the
highest priority value is selected.
+
- If there are multiple advertisements of the FAD with the same highest
priority, the FAD of the router with the highest IS-IS system-ID is
selected.
@@ -497,15 +506,11 @@ which flex algorithm they must use for a given packet.
The following commands configure Flex-Algo at the 'router isis' configuration
level. Segment-Routing prefixes must be configured for the Flex-Algo.
-.. clicmd:: flexible-algorithm (128-255)
+.. clicmd:: flex-algo (128-255)
Add a Flex-Algo Definition (FAD) and enter the FAD configuration
level. The algorithm ID value is in the range of 128 to 255 inclusive.
-.. clicmd:: no flexible-algorithm (128-255)
-
- Unconfigure a Flex-Algo Definition.
-
.. clicmd:: affinity-map NAME bit-position (0-255)
Add the specified 'affinity-map'. Affinity-map definitions are used in
@@ -517,7 +522,7 @@ level. Segment-Routing prefixes must be configured for the Flex-Algo.
admin-group 'bit-position' is set 1, else it is set to 0.
The following commands configure Flex-Algo at the 'router isis' and
-'flexible-algorithm (128-255)' configuration level.
+'flex-algo (128-255)' configuration level.
.. clicmd:: advertise-definition
diff --git a/fpm/fpm_pb.c b/fpm/fpm_pb.c
index e4c9395a84..0e8f618c4d 100644
--- a/fpm/fpm_pb.c
+++ b/fpm/fpm_pb.c
@@ -10,3 +10,8 @@
/*
* Main file for the fpm_pb library.
*/
+
+#include "config.h"
+#include "xref.h"
+
+XREF_SETUP();
diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c
index 7acd3a2b4e..430bee92bf 100644
--- a/isisd/isis_adjacency.c
+++ b/isisd/isis_adjacency.c
@@ -726,13 +726,13 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty,
now = time(NULL);
if (adj->last_upd) {
if (adj->last_upd + adj->hold_time < now)
- vty_out(vty, " Expiring");
+ vty_out(vty, " Expiring ");
else
vty_out(vty, " %-9llu",
(unsigned long long)adj->last_upd
+ adj->hold_time - now);
} else
- vty_out(vty, "- ");
+ vty_out(vty, " - ");
vty_out(vty, "%-10pSY", adj->snpa);
vty_out(vty, "\n");
}
diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c
index 77573cdfac..9d671137e9 100644
--- a/isisd/isis_lsp.c
+++ b/isisd/isis_lsp.c
@@ -822,15 +822,24 @@ int lsp_print_all(struct vty *vty, struct json_object *json,
{
struct isis_lsp *lsp;
int lsp_count = 0;
+ struct json_object *lsp_json = NULL;
if (detail == ISIS_UI_LEVEL_BRIEF) {
frr_each (lspdb, head, lsp) {
- lsp_print_common(lsp, vty, json, dynhost, isis);
+ if (json) {
+ lsp_json = json_object_new_object();
+ json_object_array_add(json, lsp_json);
+ }
+ lsp_print_common(lsp, vty, lsp_json, dynhost, isis);
lsp_count++;
}
} else if (detail == ISIS_UI_LEVEL_DETAIL) {
frr_each (lspdb, head, lsp) {
- lsp_print_detail(lsp, vty, json, dynhost, isis);
+ if (json) {
+ lsp_json = json_object_new_object();
+ json_object_array_add(json, lsp_json);
+ }
+ lsp_print_detail(lsp, vty, lsp_json, dynhost, isis);
lsp_count++;
}
}
diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c
index 294c03def1..418e0af16b 100644
--- a/isisd/isis_spf.c
+++ b/isisd/isis_spf.c
@@ -2343,9 +2343,6 @@ static void show_isis_topology_common(struct vty *vty, int levels,
return;
for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
- vty_out(vty,
- "Area %s:", area->area_tag ? area->area_tag : "null");
-
#ifndef FABRICD
/*
* The shapes of the flex algo spftree 2-dimensional array
@@ -2361,6 +2358,9 @@ static void show_isis_topology_common(struct vty *vty, int levels,
} else
fa_data = NULL;
+ vty_out(vty,
+ "Area %s:", area->area_tag ? area->area_tag : "null");
+
if (algo != SR_ALGORITHM_SPF)
vty_out(vty, " Algorithm %hhu\n", algo);
else
@@ -2469,37 +2469,33 @@ DEFUN(show_isis_topology, show_isis_topology_cmd,
}
ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
- if (vrf_name) {
- if (all_vrf) {
- for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) {
- if (all_algorithm) {
- for (algorithm = SR_ALGORITHM_FLEX_MIN;
- algorithm <= SR_ALGORITHM_FLEX_MAX;
- algorithm++)
- show_isis_topology_common(
- vty, levels, isis,
- (uint8_t)algorithm);
- } else {
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) {
+ if (all_algorithm) {
+ for (algorithm = SR_ALGORITHM_FLEX_MIN;
+ algorithm <= SR_ALGORITHM_FLEX_MAX;
+ algorithm++)
show_isis_topology_common(
vty, levels, isis,
(uint8_t)algorithm);
- }
- }
- return CMD_SUCCESS;
- }
- isis = isis_lookup_by_vrfname(vrf_name);
- if (isis == NULL)
- return CMD_SUCCESS;
- if (all_algorithm) {
- for (algorithm = SR_ALGORITHM_FLEX_MIN;
- algorithm <= SR_ALGORITHM_FLEX_MAX; algorithm++) {
+ } else {
show_isis_topology_common(vty, levels, isis,
(uint8_t)algorithm);
}
- } else
+ }
+ return CMD_SUCCESS;
+ }
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis == NULL)
+ return CMD_SUCCESS;
+ if (all_algorithm) {
+ for (algorithm = SR_ALGORITHM_FLEX_MIN;
+ algorithm <= SR_ALGORITHM_FLEX_MAX; algorithm++) {
show_isis_topology_common(vty, levels, isis,
(uint8_t)algorithm);
- }
+ }
+ } else
+ show_isis_topology_common(vty, levels, isis, (uint8_t)algorithm);
return CMD_SUCCESS;
}
@@ -2672,17 +2668,14 @@ DEFUN(show_isis_flex_algo, show_isis_flex_algo_cmd,
ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
- if (vrf_name) {
- if (all_vrf) {
- for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
- show_isis_flex_algo_common(vty, isis,
- flex_algo);
- return CMD_SUCCESS;
- }
- isis = isis_lookup_by_vrfname(vrf_name);
- if (isis != NULL)
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
show_isis_flex_algo_common(vty, isis, flex_algo);
+ return CMD_SUCCESS;
}
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL)
+ show_isis_flex_algo_common(vty, isis, flex_algo);
return CMD_SUCCESS;
}
@@ -3140,34 +3133,8 @@ DEFUN(show_isis_route, show_isis_route_cmd,
if (uj)
json = json_object_new_array();
- if (vrf_name) {
- if (all_vrf) {
- for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) {
- if (all_algorithm)
- show_isis_route_all_algos(vty, levels,
- isis,
- prefix_sid,
- backup,
- uj ? &json_vrf
- : NULL);
- else
- show_isis_route_common(vty, levels,
- isis, prefix_sid,
- backup, algorithm,
- uj ? &json_vrf
- : NULL);
- if (uj) {
- json_object_object_add(
- json_vrf, "vrf_id",
- json_object_new_int(
- isis->vrf_id));
- json_object_array_add(json, json_vrf);
- }
- }
- goto out;
- }
- isis = isis_lookup_by_vrfname(vrf_name);
- if (isis != NULL) {
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) {
if (all_algorithm)
show_isis_route_all_algos(vty, levels, isis,
prefix_sid, backup,
@@ -3178,12 +3145,28 @@ DEFUN(show_isis_route, show_isis_route_cmd,
algorithm,
uj ? &json_vrf : NULL);
if (uj) {
- json_object_object_add(
- json_vrf, "vrf_id",
- json_object_new_int(isis->vrf_id));
+ json_object_object_add(json_vrf, "vrf_id",
+ json_object_new_int(
+ isis->vrf_id));
json_object_array_add(json, json_vrf);
}
}
+ goto out;
+ }
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL) {
+ if (all_algorithm)
+ show_isis_route_all_algos(vty, levels, isis, prefix_sid,
+ backup, uj ? &json_vrf : NULL);
+ else
+ show_isis_route_common(vty, levels, isis, prefix_sid,
+ backup, algorithm,
+ uj ? &json_vrf : NULL);
+ if (uj) {
+ json_object_object_add(json_vrf, "vrf_id",
+ json_object_new_int(isis->vrf_id));
+ json_object_array_add(json, json_vrf);
+ }
}
out:
@@ -3395,16 +3378,14 @@ DEFUN(show_isis_frr_summary, show_isis_frr_summary_cmd,
}
ISIS_FIND_VRF_ARGS(argv, argc, idx, vrf_name, all_vrf);
- if (vrf_name) {
- if (all_vrf) {
- for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
- show_isis_frr_summary_common(vty, levels, isis);
- return CMD_SUCCESS;
- }
- isis = isis_lookup_by_vrfname(vrf_name);
- if (isis != NULL)
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
show_isis_frr_summary_common(vty, levels, isis);
+ return CMD_SUCCESS;
}
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL)
+ show_isis_frr_summary_common(vty, levels, isis);
return CMD_SUCCESS;
}
diff --git a/isisd/isis_te.c b/isisd/isis_te.c
index 61c3db7f05..3683f74558 100644
--- a/isisd/isis_te.c
+++ b/isisd/isis_te.c
@@ -1690,30 +1690,26 @@ DEFUN(show_isis_mpls_te_router,
return CMD_SUCCESS;
}
ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
- if (vrf_name) {
- if (all_vrf) {
- for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) {
- for (ALL_LIST_ELEMENTS_RO(isis->area_list,
- anode, area)) {
- if (!IS_MPLS_TE(area->mta))
- continue;
-
- show_router_id(vty, area);
- }
- }
- return 0;
- }
- isis = isis_lookup_by_vrfname(vrf_name);
- if (isis != NULL) {
- for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode,
- area)) {
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) {
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
if (!IS_MPLS_TE(area->mta))
continue;
show_router_id(vty, area);
}
}
+ return 0;
+ }
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL) {
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
+ if (!IS_MPLS_TE(area->mta))
+ continue;
+
+ show_router_id(vty, area);
+ }
}
return CMD_SUCCESS;
@@ -2162,19 +2158,18 @@ DEFUN(show_isis_mpls_te_db,
int rc = CMD_WARNING;
ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
- if (vrf_name) {
- if (all_vrf) {
- for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) {
- rc = show_isis_ted(vty, argv, argc, isis);
- if (rc != CMD_SUCCESS)
- return rc;
- }
- return CMD_SUCCESS;
- }
- isis = isis_lookup_by_vrfname(vrf_name);
- if (isis)
+
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) {
rc = show_isis_ted(vty, argv, argc, isis);
+ if (rc != CMD_SUCCESS)
+ return rc;
+ }
+ return CMD_SUCCESS;
}
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis)
+ rc = show_isis_ted(vty, argv, argc, isis);
return rc;
}
diff --git a/isisd/isisd.c b/isisd/isisd.c
index 772eb9708d..382a6aa3be 100644
--- a/isisd/isisd.c
+++ b/isisd/isisd.c
@@ -988,63 +988,17 @@ int show_isis_interface_common_json(struct json_object *json,
"no");
return CMD_SUCCESS;
}
- if (vrf_name) {
- if (all_vrf) {
- for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) {
- areas_json = json_object_new_array();
- json_object_object_add(json, "areas",
- areas_json);
- for (ALL_LIST_ELEMENTS_RO(isis->area_list,
- anode, area)) {
- area_json = json_object_new_object();
- json_object_string_add(
- area_json, "area",
- area->area_tag ? area->area_tag
- : "null");
- circuits_json = json_object_new_array();
- json_object_object_add(area_json,
- "circuits",
- circuits_json);
- for (ALL_LIST_ELEMENTS_RO(
- area->circuit_list, cnode,
- circuit)) {
- circuit_json =
- json_object_new_object();
- json_object_int_add(
- circuit_json, "circuit",
- circuit->circuit_id);
- if (!ifname)
- isis_circuit_print_json(
- circuit,
- circuit_json,
- detail);
- else if (strcmp(circuit->interface->name, ifname) == 0)
- isis_circuit_print_json(
- circuit,
- circuit_json,
- detail);
- json_object_array_add(
- circuits_json,
- circuit_json);
- }
- json_object_array_add(areas_json,
- area_json);
- }
- }
- return CMD_SUCCESS;
- }
- isis = isis_lookup_by_vrfname(vrf_name);
- if (isis != NULL) {
+
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) {
areas_json = json_object_new_array();
json_object_object_add(json, "areas", areas_json);
- for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode,
- area)) {
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
area_json = json_object_new_object();
json_object_string_add(area_json, "area",
area->area_tag
? area->area_tag
: "null");
-
circuits_json = json_object_new_array();
json_object_object_add(area_json, "circuits",
circuits_json);
@@ -1055,22 +1009,56 @@ int show_isis_interface_common_json(struct json_object *json,
circuit_json, "circuit",
circuit->circuit_id);
if (!ifname)
- isis_circuit_print_json(
- circuit, circuit_json,
- detail);
- else if (
- strcmp(circuit->interface->name,
- ifname) == 0)
- isis_circuit_print_json(
- circuit, circuit_json,
- detail);
+ isis_circuit_print_json(circuit,
+ circuit_json,
+ detail);
+ else if (strcmp(circuit->interface->name,
+ ifname) == 0)
+ isis_circuit_print_json(circuit,
+ circuit_json,
+ detail);
json_object_array_add(circuits_json,
circuit_json);
}
json_object_array_add(areas_json, area_json);
}
}
+ return CMD_SUCCESS;
+ }
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL) {
+ areas_json = json_object_new_array();
+ json_object_object_add(json, "areas", areas_json);
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
+ area_json = json_object_new_object();
+ json_object_string_add(area_json, "area",
+ area->area_tag ? area->area_tag
+ : "null");
+
+ circuits_json = json_object_new_array();
+ json_object_object_add(area_json, "circuits",
+ circuits_json);
+ for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode,
+ circuit)) {
+ circuit_json = json_object_new_object();
+ json_object_int_add(circuit_json, "circuit",
+ circuit->circuit_id);
+ if (!ifname)
+ isis_circuit_print_json(circuit,
+ circuit_json,
+ detail);
+ else if (strcmp(circuit->interface->name,
+ ifname) == 0)
+ isis_circuit_print_json(circuit,
+ circuit_json,
+ detail);
+ json_object_array_add(circuits_json,
+ circuit_json);
+ }
+ json_object_array_add(areas_json, area_json);
+ }
}
+
return CMD_SUCCESS;
}
@@ -1087,37 +1075,10 @@ int show_isis_interface_common_vty(struct vty *vty, const char *ifname,
vty_out(vty, "IS-IS Routing Process not enabled\n");
return CMD_SUCCESS;
}
- if (vrf_name) {
- if (all_vrf) {
- for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) {
- for (ALL_LIST_ELEMENTS_RO(isis->area_list,
- anode, area)) {
- vty_out(vty, "Area %s:\n",
- area->area_tag);
- if (detail == ISIS_UI_LEVEL_BRIEF)
- vty_out(vty,
- " Interface CircId State Type Level\n");
-
- for (ALL_LIST_ELEMENTS_RO(
- area->circuit_list, cnode,
- circuit))
- if (!ifname)
- isis_circuit_print_vty(
- circuit, vty,
- detail);
- else if (strcmp(circuit->interface->name, ifname) == 0)
- isis_circuit_print_vty(
- circuit, vty,
- detail);
- }
- }
- return CMD_SUCCESS;
- }
- isis = isis_lookup_by_vrfname(vrf_name);
- if (isis != NULL) {
- for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode,
- area)) {
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) {
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
vty_out(vty, "Area %s:\n", area->area_tag);
if (detail == ISIS_UI_LEVEL_BRIEF)
@@ -1127,15 +1088,37 @@ int show_isis_interface_common_vty(struct vty *vty, const char *ifname,
for (ALL_LIST_ELEMENTS_RO(area->circuit_list,
cnode, circuit))
if (!ifname)
- isis_circuit_print_vty(
- circuit, vty, detail);
- else if (
- strcmp(circuit->interface->name,
- ifname) == 0)
- isis_circuit_print_vty(
- circuit, vty, detail);
+ isis_circuit_print_vty(circuit,
+ vty,
+ detail);
+ else if (strcmp(circuit->interface->name,
+ ifname) == 0)
+ isis_circuit_print_vty(circuit,
+ vty,
+ detail);
}
}
+ return CMD_SUCCESS;
+ }
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL) {
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
+ vty_out(vty, "Area %s:\n", area->area_tag);
+
+ if (detail == ISIS_UI_LEVEL_BRIEF)
+ vty_out(vty,
+ " Interface CircId State Type Level\n");
+
+ for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode,
+ circuit))
+ if (!ifname)
+ isis_circuit_print_vty(circuit, vty,
+ detail);
+ else if (strcmp(circuit->interface->name,
+ ifname) == 0)
+ isis_circuit_print_vty(circuit, vty,
+ detail);
+ }
}
return CMD_SUCCESS;
@@ -1318,7 +1301,7 @@ static void isis_neighbor_common_vty(struct vty *vty, const char *id,
if (detail == ISIS_UI_LEVEL_BRIEF)
vty_out(vty,
- " System Id Interface L State Holdtime SNPA\n");
+ " System Id Interface L State Holdtime SNPA\n");
for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit)) {
if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
@@ -1376,28 +1359,23 @@ int show_isis_neighbor_common(struct vty *vty, struct json_object *json,
return CMD_SUCCESS;
}
- if (vrf_name) {
- if (all_vrf) {
- for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) {
- if (id_to_sysid(isis, id, sysid)) {
- vty_out(vty, "Invalid system id %s\n",
- id);
- return CMD_SUCCESS;
- }
- isis_neighbor_common(vty, json, id, detail,
- isis, sysid);
- }
- return CMD_SUCCESS;
- }
- isis = isis_lookup_by_vrfname(vrf_name);
- if (isis != NULL) {
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) {
if (id_to_sysid(isis, id, sysid)) {
vty_out(vty, "Invalid system id %s\n", id);
return CMD_SUCCESS;
}
- isis_neighbor_common(vty, json, id, detail, isis,
- sysid);
+ isis_neighbor_common(vty, json, id, detail, isis, sysid);
}
+ return CMD_SUCCESS;
+ }
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL) {
+ if (id_to_sysid(isis, id, sysid)) {
+ vty_out(vty, "Invalid system id %s\n", id);
+ return CMD_SUCCESS;
+ }
+ isis_neighbor_common(vty, json, id, detail, isis, sysid);
}
return CMD_SUCCESS;
@@ -1461,27 +1439,23 @@ int clear_isis_neighbor_common(struct vty *vty, const char *id, const char *vrf_
return CMD_SUCCESS;
}
- if (vrf_name) {
- if (all_vrf) {
- for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) {
- if (id_to_sysid(isis, id, sysid)) {
- vty_out(vty, "Invalid system id %s\n",
- id);
- return CMD_SUCCESS;
- }
- isis_neighbor_common_clear(vty, id, sysid,
- isis);
- }
- return CMD_SUCCESS;
- }
- isis = isis_lookup_by_vrfname(vrf_name);
- if (isis != NULL) {
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) {
if (id_to_sysid(isis, id, sysid)) {
vty_out(vty, "Invalid system id %s\n", id);
return CMD_SUCCESS;
}
isis_neighbor_common_clear(vty, id, sysid, isis);
}
+ return CMD_SUCCESS;
+ }
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL) {
+ if (id_to_sysid(isis, id, sysid)) {
+ vty_out(vty, "Invalid system id %s\n", id);
+ return CMD_SUCCESS;
+ }
+ isis_neighbor_common_clear(vty, id, sysid, isis);
}
return CMD_SUCCESS;
@@ -2234,17 +2208,16 @@ DEFUN (show_hostname,
struct isis *isis;
ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
- if (vrf_name) {
- if (all_vrf) {
- for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
- dynhn_print_all(vty, isis);
- return CMD_SUCCESS;
- }
- isis = isis_lookup_by_vrfname(vrf_name);
- if (isis != NULL)
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
dynhn_print_all(vty, isis);
+
+ return CMD_SUCCESS;
}
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL)
+ dynhn_print_all(vty, isis);
return CMD_SUCCESS;
}
@@ -2307,17 +2280,15 @@ DEFUN(show_isis_spf_ietf, show_isis_spf_ietf_cmd,
return CMD_SUCCESS;
}
- if (vrf_name) {
- if (all_vrf) {
- for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
- isis_spf_ietf_common(vty, isis);
-
- return CMD_SUCCESS;
- }
- isis = isis_lookup_by_vrfname(vrf_name);
- if (isis != NULL)
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
isis_spf_ietf_common(vty, isis);
+
+ return CMD_SUCCESS;
}
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL)
+ isis_spf_ietf_common(vty, isis);
return CMD_SUCCESS;
}
@@ -2596,17 +2567,16 @@ DEFUN(show_isis_summary, show_isis_summary_cmd,
}
if (uj)
json = json_object_new_object();
- if (vrf_name) {
- if (all_vrf) {
- for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
- common_isis_summary(vty, json, isis);
- return CMD_SUCCESS;
- }
- isis = isis_lookup_by_vrfname(vrf_name);
- if (isis != NULL)
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
common_isis_summary(vty, json, isis);
+
+ return CMD_SUCCESS;
}
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL)
+ common_isis_summary(vty, json, isis);
if (uj)
vty_json(vty, json);
@@ -2681,6 +2651,7 @@ void show_isis_database_lspdb_json(struct json_object *json,
{
struct isis_lsp *lsp;
int lsp_count;
+ struct json_object *lsp_arr_json;
if (lspdb_count(lspdb) > 0) {
lsp = lsp_for_sysid(lspdb, sysid_str, area->isis);
@@ -2697,9 +2668,12 @@ void show_isis_database_lspdb_json(struct json_object *json,
lsp_print_json(lsp, json, area->dynhostname,
area->isis);
} else if (sysid_str == NULL) {
- lsp_count =
- lsp_print_all(NULL, json, lspdb, ui_level,
- area->dynhostname, area->isis);
+ lsp_arr_json = json_object_new_array();
+ json_object_object_add(json, "lsps", lsp_arr_json);
+
+ lsp_count = lsp_print_all(NULL, lsp_arr_json, lspdb,
+ ui_level, area->dynhostname,
+ area->isis);
json_object_int_add(json, "count", lsp_count);
}
@@ -2828,19 +2802,16 @@ static int show_isis_database(struct vty *vty, struct json_object *json, const c
struct listnode *node;
struct isis *isis;
- if (vrf_name) {
- if (all_vrf) {
- for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
- show_isis_database_common(vty, json, sysid_str,
- ui_level, isis);
-
- return CMD_SUCCESS;
- }
- isis = isis_lookup_by_vrfname(vrf_name);
- if (isis)
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
show_isis_database_common(vty, json, sysid_str,
ui_level, isis);
+
+ return CMD_SUCCESS;
}
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis)
+ show_isis_database_common(vty, json, sysid_str, ui_level, isis);
return CMD_SUCCESS;
}
diff --git a/lib/elf_py.c b/lib/elf_py.c
index 643495d8c7..2b4fea373f 100644
--- a/lib/elf_py.c
+++ b/lib/elf_py.c
@@ -1358,6 +1358,15 @@ bool elf_py_init(PyObject *pymod)
(void)methods_elfpy;
#endif
+#if defined(HAVE_GELF_GETNOTE) && defined(HAVE_ELF_GETDATA_RAWCHUNK)
+ PyObject *elf_notes = Py_True;
+#else
+ PyObject *elf_notes = Py_False;
+#endif
+ Py_INCREF(elf_notes);
+ if (PyModule_AddObject(pymod, "elf_notes", elf_notes))
+ Py_DECREF(elf_notes);
+
ELFFormatError = PyErr_NewException("_clippy.ELFFormatError",
PyExc_ValueError, NULL);
PyModule_AddObject(pymod, "ELFFormatError", ELFFormatError);
diff --git a/lib/frr_zmq.c b/lib/frr_zmq.c
index b28dd7f1bb..5273d36974 100644
--- a/lib/frr_zmq.c
+++ b/lib/frr_zmq.c
@@ -21,6 +21,8 @@
#include "log.h"
#include "lib_errors.h"
+XREF_SETUP();
+
DEFINE_MTYPE_STATIC(LIB, ZEROMQ_CB, "ZeroMQ callback");
/* libzmq's context */
diff --git a/lib/if.c b/lib/if.c
index a8ceac7243..bf14baaa5e 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -885,21 +885,6 @@ nbr_connected_log(struct nbr_connected *connected, char *str)
zlog_info("%s", logbuf);
}
-/* If two connected address has same prefix return 1. */
-static int connected_same_prefix(const struct prefix *p1,
- const struct prefix *p2)
-{
- if (p1->family == p2->family) {
- if (p1->family == AF_INET
- && IPV4_ADDR_SAME(&p1->u.prefix4, &p2->u.prefix4))
- return 1;
- if (p1->family == AF_INET6
- && IPV6_ADDR_SAME(&p1->u.prefix6, &p2->u.prefix6))
- return 1;
- }
- return 0;
-}
-
/* count the number of connected addresses that are in the given family */
unsigned int connected_count_by_family(struct interface *ifp, int family)
{
@@ -919,7 +904,7 @@ struct connected *connected_lookup_prefix_exact(struct interface *ifp,
struct connected *ifc;
frr_each (if_connected, ifp->connected, ifc) {
- if (connected_same_prefix(ifc->address, p))
+ if (prefix_same(ifc->address, p))
return ifc;
}
return NULL;
@@ -932,7 +917,7 @@ struct connected *connected_delete_by_prefix(struct interface *ifp,
/* In case of same prefix come, replace it with new one. */
frr_each_safe (if_connected, ifp->connected, ifc) {
- if (connected_same_prefix(ifc->address, p)) {
+ if (prefix_same(ifc->address, p)) {
if_connected_del(ifp->connected, ifc);
return ifc;
}
diff --git a/lib/mgmt.proto b/lib/mgmt.proto
index 01a99ab63b..c95301118b 100644
--- a/lib/mgmt.proto
+++ b/lib/mgmt.proto
@@ -79,6 +79,7 @@ message BeSubscribeReq {
repeated string config_xpaths = 2;
repeated string oper_xpaths = 3;
repeated string notif_xpaths = 4;
+ repeated string rpc_xpaths = 5;
}
message BeSubscribeReply {
diff --git a/lib/mgmt_be_client.c b/lib/mgmt_be_client.c
index f483d48d8d..6e2fb05e84 100644
--- a/lib/mgmt_be_client.c
+++ b/lib/mgmt_be_client.c
@@ -915,6 +915,143 @@ static void be_client_handle_get_tree(struct mgmt_be_client *client,
be_client_send_tree_data_batch, args);
}
+static void be_client_send_rpc_reply(struct mgmt_be_client *client,
+ uint64_t txn_id, uint64_t req_id,
+ uint8_t result_type,
+ struct lyd_node *output)
+{
+ struct mgmt_msg_rpc_reply *rpc_reply_msg;
+ uint8_t **darrp;
+ LY_ERR err;
+ int ret = NB_OK;
+
+ rpc_reply_msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_rpc_reply, 0,
+ MTYPE_MSG_NATIVE_RPC_REPLY);
+ rpc_reply_msg->refer_id = txn_id;
+ rpc_reply_msg->req_id = req_id;
+ rpc_reply_msg->code = MGMT_MSG_CODE_RPC_REPLY;
+ rpc_reply_msg->result_type = result_type;
+
+ if (output) {
+ darrp = mgmt_msg_native_get_darrp(rpc_reply_msg);
+ err = yang_print_tree_append(darrp, output, result_type,
+ LYD_PRINT_SHRINK);
+ lyd_free_all(output);
+ if (err) {
+ ret = NB_ERR;
+ goto done;
+ }
+ }
+
+ (void)be_client_send_native_msg(client, rpc_reply_msg,
+ mgmt_msg_native_get_msg_len(
+ rpc_reply_msg),
+ false);
+done:
+ mgmt_msg_native_free_msg(rpc_reply_msg);
+ if (ret != NB_OK)
+ be_client_send_error(client, txn_id, req_id, false, -EINVAL,
+ "Can't format RPC reply");
+}
+
+/*
+ * Process the RPC request.
+ */
+static void be_client_handle_rpc(struct mgmt_be_client *client, uint64_t txn_id,
+ void *msgbuf, size_t msg_len)
+{
+ struct mgmt_msg_rpc *rpc_msg = msgbuf;
+ struct nb_node *nb_node;
+ struct lyd_node *input, *output;
+ const char *xpath;
+ const char *data;
+ char errmsg[BUFSIZ] = { 0 };
+ LY_ERR err;
+ int ret;
+
+ debug_be_client("Received RPC request for client %s txn-id %" PRIu64
+ " req-id %" PRIu64,
+ client->name, txn_id, rpc_msg->req_id);
+
+ xpath = mgmt_msg_native_xpath_data_decode(rpc_msg, msg_len, data);
+ if (!xpath) {
+ be_client_send_error(client, txn_id, rpc_msg->req_id, false,
+ -EINVAL, "Corrupt RPC message");
+ return;
+ }
+
+ nb_node = nb_node_find(xpath);
+ if (!nb_node) {
+ be_client_send_error(client, txn_id, rpc_msg->req_id, false,
+ -EINVAL, "No schema found for RPC: %s",
+ xpath);
+ return;
+ }
+
+ if (!nb_node->cbs.rpc) {
+ be_client_send_error(client, txn_id, rpc_msg->req_id, false,
+ -EINVAL, "No RPC callback for: %s", xpath);
+ return;
+ }
+
+ if (data) {
+ err = yang_parse_rpc(xpath, rpc_msg->request_type, data, false,
+ &input);
+ if (err) {
+ be_client_send_error(client, txn_id, rpc_msg->req_id,
+ false, -EINVAL,
+ "Can't parse RPC data for: %s",
+ xpath);
+ return;
+ }
+ } else {
+ /*
+ * If there's no input data, create an empty input container.
+ * It is especially needed for actions, because their parents
+ * may hold necessary information.
+ */
+ err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, 0,
+ NULL, &input);
+ if (err) {
+ be_client_send_error(client, txn_id, rpc_msg->req_id,
+ false, -EINVAL,
+ "Can't create input node for RPC: %s",
+ xpath);
+ return;
+ }
+ }
+
+ err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, 0, NULL,
+ &output);
+ if (err) {
+ lyd_free_all(input);
+ be_client_send_error(client, txn_id, rpc_msg->req_id, false,
+ -EINVAL,
+ "Can't create output node for RPC: %s",
+ xpath);
+ return;
+ }
+
+ ret = nb_callback_rpc(nb_node, xpath, input, output, errmsg,
+ sizeof(errmsg));
+ if (ret != NB_OK) {
+ lyd_free_all(input);
+ lyd_free_all(output);
+ be_client_send_error(client, txn_id, rpc_msg->req_id, false,
+ -EINVAL, "%s", errmsg);
+ return;
+ }
+
+ lyd_free_all(input);
+ if (!lyd_child(output)) {
+ lyd_free_all(output);
+ output = NULL;
+ }
+
+ be_client_send_rpc_reply(client, txn_id, rpc_msg->req_id,
+ rpc_msg->request_type, output);
+}
+
/*
* Process the notification.
*/
@@ -975,6 +1112,9 @@ static void be_client_handle_native_msg(struct mgmt_be_client *client,
case MGMT_MSG_CODE_GET_TREE:
be_client_handle_get_tree(client, txn_id, msg, msg_len);
break;
+ case MGMT_MSG_CODE_RPC:
+ be_client_handle_rpc(client, txn_id, msg, msg_len);
+ break;
case MGMT_MSG_CODE_NOTIFY:
be_client_handle_notify(client, msg, msg_len);
break;
@@ -1040,6 +1180,9 @@ int mgmt_be_send_subscr_req(struct mgmt_be_client *client_ctx,
subscr_req.n_notif_xpaths = client_ctx->cbs.nnotif_xpaths;
subscr_req.notif_xpaths = (char **)client_ctx->cbs.notif_xpaths;
+ subscr_req.n_rpc_xpaths = client_ctx->cbs.nrpc_xpaths;
+ subscr_req.rpc_xpaths = (char **)client_ctx->cbs.rpc_xpaths;
+
mgmtd__be_message__init(&be_msg);
be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ;
be_msg.subscr_req = &subscr_req;
diff --git a/lib/mgmt_be_client.h b/lib/mgmt_be_client.h
index cd8b237526..7ad0589bdb 100644
--- a/lib/mgmt_be_client.h
+++ b/lib/mgmt_be_client.h
@@ -75,6 +75,8 @@ struct mgmt_be_client_cbs {
const char **notif_xpaths;
uint nnotif_xpaths;
+ const char **rpc_xpaths;
+ uint nrpc_xpaths;
};
/***************************************************************
diff --git a/lib/mgmt_fe_client.c b/lib/mgmt_fe_client.c
index 3345505213..8cfb025f72 100644
--- a/lib/mgmt_fe_client.c
+++ b/lib/mgmt_fe_client.c
@@ -360,6 +360,33 @@ int mgmt_fe_send_edit_req(struct mgmt_fe_client *client, uint64_t session_id,
return ret;
}
+int mgmt_fe_send_rpc_req(struct mgmt_fe_client *client, uint64_t session_id,
+ uint64_t req_id, LYD_FORMAT request_type,
+ const char *xpath, const char *data)
+{
+ struct mgmt_msg_rpc *msg;
+ int ret;
+
+ msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_rpc, 0,
+ MTYPE_MSG_NATIVE_RPC);
+ msg->refer_id = session_id;
+ msg->req_id = req_id;
+ msg->code = MGMT_MSG_CODE_RPC;
+ msg->request_type = request_type;
+
+ mgmt_msg_native_xpath_encode(msg, xpath);
+ if (data)
+ mgmt_msg_native_append(msg, data, strlen(data) + 1);
+
+ debug_fe_client("Sending RPC_REQ session-id %" PRIu64 " req-id %" PRIu64
+ " xpath: %s",
+ session_id, req_id, xpath);
+
+ ret = mgmt_msg_native_send_msg(&client->client.conn, msg, false);
+ mgmt_msg_native_free_msg(msg);
+ return ret;
+}
+
static int mgmt_fe_client_handle_msg(struct mgmt_fe_client *client,
Mgmtd__FeMessage *fe_msg)
{
@@ -534,6 +561,7 @@ static void fe_client_handle_native_msg(struct mgmt_fe_client *client,
struct mgmt_msg_notify_data *notify_msg;
struct mgmt_msg_tree_data *tree_msg;
struct mgmt_msg_edit_reply *edit_msg;
+ struct mgmt_msg_rpc_reply *rpc_msg;
struct mgmt_msg_error *err_msg;
const char *xpath = NULL;
const char *data = NULL;
@@ -608,6 +636,23 @@ static void fe_client_handle_native_msg(struct mgmt_fe_client *client,
session->user_ctx, msg->req_id,
xpath);
break;
+ case MGMT_MSG_CODE_RPC_REPLY:
+ if (!session->client->cbs.rpc_notify)
+ return;
+
+ rpc_msg = (typeof(rpc_msg))msg;
+ if (msg_len < sizeof(*rpc_msg)) {
+ log_err_fe_client("Corrupt rpc-reply msg recv");
+ return;
+ }
+ dlen = msg_len - sizeof(*rpc_msg);
+
+ session->client->cbs.rpc_notify(client, client->user_data,
+ session->client_id,
+ msg->refer_id,
+ session->user_ctx, msg->req_id,
+ dlen ? rpc_msg->data : NULL);
+ break;
case MGMT_MSG_CODE_NOTIFY:
if (!session->client->cbs.async_notification)
return;
diff --git a/lib/mgmt_fe_client.h b/lib/mgmt_fe_client.h
index 9d569348ae..20c87044a5 100644
--- a/lib/mgmt_fe_client.h
+++ b/lib/mgmt_fe_client.h
@@ -120,6 +120,12 @@ struct mgmt_fe_client_cbs {
uintptr_t session_ctx, uint64_t req_id,
const char *xpath);
+ /* Called when RPC result is returned */
+ int (*rpc_notify)(struct mgmt_fe_client *client, uintptr_t user_data,
+ uint64_t client_id, uint64_t session_id,
+ uintptr_t session_ctx, uint64_t req_id,
+ const char *result);
+
/* Called with asynchronous notifications from backends */
int (*async_notification)(struct mgmt_fe_client *client,
uintptr_t user_data, uint64_t client_id,
@@ -455,6 +461,35 @@ extern int mgmt_fe_send_edit_req(struct mgmt_fe_client *client,
const char *xpath, const char *data);
/*
+ * Send RPC request to MGMTD daemon.
+ *
+ * client
+ * Client object.
+ *
+ * session_id
+ * Client session ID.
+ *
+ * req_id
+ * Client request ID.
+ *
+ * result_type
+ * The LYD_FORMAT of the result.
+ *
+ * xpath
+ * the xpath of the RPC.
+ *
+ * data
+ * the data tree.
+ *
+ * Returns:
+ * 0 on success, otherwise msg_conn_send_msg() return values.
+ */
+extern int mgmt_fe_send_rpc_req(struct mgmt_fe_client *client,
+ uint64_t session_id, uint64_t req_id,
+ LYD_FORMAT request_type, const char *xpath,
+ const char *data);
+
+/*
* Destroy library and cleanup everything.
*/
extern void mgmt_fe_client_destroy(struct mgmt_fe_client *client);
diff --git a/lib/mgmt_msg_native.c b/lib/mgmt_msg_native.c
index 09ea43ece0..39ce9abae6 100644
--- a/lib/mgmt_msg_native.c
+++ b/lib/mgmt_msg_native.c
@@ -16,6 +16,8 @@ DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_GET_DATA, "native get data msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_NOTIFY, "native get data msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_EDIT, "native edit msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_EDIT_REPLY, "native edit reply msg");
+DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_RPC, "native RPC msg");
+DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_RPC_REPLY, "native RPC reply msg");
int vmgmt_msg_native_send_error(struct msg_conn *conn, uint64_t sess_or_txn_id,
uint64_t req_id, bool short_circuit_ok,
diff --git a/lib/mgmt_msg_native.h b/lib/mgmt_msg_native.h
index b7c29862aa..cf528a6356 100644
--- a/lib/mgmt_msg_native.h
+++ b/lib/mgmt_msg_native.h
@@ -152,6 +152,8 @@ DECLARE_MTYPE(MSG_NATIVE_GET_DATA);
DECLARE_MTYPE(MSG_NATIVE_NOTIFY);
DECLARE_MTYPE(MSG_NATIVE_EDIT);
DECLARE_MTYPE(MSG_NATIVE_EDIT_REPLY);
+DECLARE_MTYPE(MSG_NATIVE_RPC);
+DECLARE_MTYPE(MSG_NATIVE_RPC_REPLY);
/*
* Native message codes
@@ -163,6 +165,8 @@ DECLARE_MTYPE(MSG_NATIVE_EDIT_REPLY);
#define MGMT_MSG_CODE_NOTIFY 4
#define MGMT_MSG_CODE_EDIT 5
#define MGMT_MSG_CODE_EDIT_REPLY 6
+#define MGMT_MSG_CODE_RPC 7
+#define MGMT_MSG_CODE_RPC_REPLY 8
/*
* Datastores
@@ -377,6 +381,42 @@ _Static_assert(sizeof(struct mgmt_msg_edit_reply) ==
offsetof(struct mgmt_msg_edit_reply, data),
"Size mismatch");
+/**
+ * struct mgmt_msg_rpc - RPC/action request.
+ *
+ * @request_type: ``LYD_FORMAT`` for the @data.
+ * @data: the xpath followed by the tree data for the operation.
+ */
+struct mgmt_msg_rpc {
+ struct mgmt_msg_header;
+ uint8_t request_type;
+ uint8_t resv2[7];
+
+ alignas(8) char data[];
+};
+
+_Static_assert(sizeof(struct mgmt_msg_rpc) ==
+ offsetof(struct mgmt_msg_rpc, data),
+ "Size mismatch");
+
+/**
+ * struct mgmt_msg_rpc_reply - RPC/action reply.
+ *
+ * @result_type: ``LYD_FORMAT`` for the @data.
+ * @data: the tree data for the reply.
+ */
+struct mgmt_msg_rpc_reply {
+ struct mgmt_msg_header;
+ uint8_t result_type;
+ uint8_t resv2[7];
+
+ alignas(8) char data[];
+};
+
+_Static_assert(sizeof(struct mgmt_msg_rpc_reply) ==
+ offsetof(struct mgmt_msg_rpc_reply, data),
+ "Size mismatch");
+
/*
* Validate that the message ends in a NUL terminating byte
*/
@@ -569,7 +609,10 @@ extern int vmgmt_msg_native_send_error(struct msg_conn *conn,
const char *__s = NULL; \
if (msg->vsplit && msg->vsplit <= __len && \
msg->data[msg->vsplit - 1] == 0) { \
- (__data) = msg->data + msg->vsplit; \
+ if (msg->vsplit < __len) \
+ (__data) = msg->data + msg->vsplit; \
+ else \
+ (__data) = NULL; \
__s = msg->data; \
} \
__s; \
diff --git a/lib/northbound.c b/lib/northbound.c
index 9a5d67cd1b..0bc79d0277 100644
--- a/lib/northbound.c
+++ b/lib/northbound.c
@@ -932,14 +932,22 @@ static int nb_candidate_edit_tree_add(struct nb_config *candidate,
/* if replace failed, restore the original node */
if (existing) {
if (root) {
+ /* Restoring the whole config. */
candidate->dnode = existing;
+ } else if (ex_parent) {
+ /*
+ * Restoring a nested node. Insert it as a
+ * child.
+ */
+ lyd_insert_child(ex_parent, existing);
} else {
- if (ex_parent)
- lyd_insert_child(ex_parent, existing);
- else
- lyd_insert_sibling(candidate->dnode,
- existing,
- &candidate->dnode);
+ /*
+ * Restoring a top-level node. Insert it as a
+ * sibling to candidate->dnode to make sure
+ * the linkage is correct.
+ */
+ lyd_insert_sibling(candidate->dnode, existing,
+ &candidate->dnode);
}
}
yang_print_errors(ly_native_ctx, errmsg, errmsg_len);
@@ -1820,14 +1828,11 @@ const void *nb_callback_lookup_next(const struct nb_node *nb_node,
}
int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
- const struct list *input, struct list *output, char *errmsg,
- size_t errmsg_len)
+ const struct lyd_node *input, struct lyd_node *output,
+ char *errmsg, size_t errmsg_len)
{
struct nb_cb_rpc_args args = {};
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
- return 0;
-
DEBUGD(&nb_dbg_cbs_rpc, "northbound RPC: %s", xpath);
args.xpath = xpath;
diff --git a/lib/northbound.h b/lib/northbound.h
index 3bf1eacd61..34d17a587c 100644
--- a/lib/northbound.h
+++ b/lib/northbound.h
@@ -274,11 +274,11 @@ struct nb_cb_rpc_args {
/* XPath of the YANG RPC or action. */
const char *xpath;
- /* Read-only list of input parameters. */
- const struct list *input;
+ /* Read-only "input" tree of the RPC/action. */
+ const struct lyd_node *input;
- /* List of output parameters to be populated by the callback. */
- struct list *output;
+ /* The "output" tree of the RPC/action to be populated by the callback. */
+ struct lyd_node *output;
/* Buffer to store human-readable error message in case of error. */
char *errmsg;
@@ -833,7 +833,7 @@ extern const void *nb_callback_lookup_next(const struct nb_node *nb_node,
const void *parent_list_entry,
const struct yang_list_keys *keys);
extern int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
- const struct list *input, struct list *output,
+ const struct lyd_node *input, struct lyd_node *output,
char *errmsg, size_t errmsg_len);
extern void nb_callback_notify(const struct nb_node *nb_node, const char *xpath,
struct lyd_node *dnode);
diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c
index 8809ec2ad8..4f962cda5c 100644
--- a/lib/northbound_cli.c
+++ b/lib/northbound_cli.c
@@ -275,10 +275,31 @@ int nb_cli_apply_changes_clear_pending(struct vty *vty,
return nb_cli_apply_changes_internal(vty, xpath_base_abs, true);
}
-int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input,
- struct list *output)
+int nb_cli_rpc_enqueue(struct vty *vty, const char *xpath, const char *value)
+{
+ struct nb_cfg_change *param;
+
+ if (vty->num_rpc_params == VTY_MAXCFGCHANGES) {
+ /* Not expected to happen. */
+ vty_out(vty,
+ "%% Exceeded the maximum number of params (%u) for a single command\n\n",
+ VTY_MAXCFGCHANGES);
+ return CMD_WARNING;
+ }
+
+ param = &vty->rpc_params[vty->num_rpc_params++];
+ strlcpy(param->xpath, xpath, sizeof(param->xpath));
+ param->value = value;
+
+ return CMD_SUCCESS;
+}
+
+int nb_cli_rpc(struct vty *vty, const char *xpath, struct lyd_node **output_p)
{
struct nb_node *nb_node;
+ struct lyd_node *input = NULL;
+ struct lyd_node *output = NULL;
+ LY_ERR err;
int ret;
char errmsg[BUFSIZ] = {0};
@@ -289,12 +310,62 @@ int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input,
return CMD_WARNING;
}
+ /* create input tree */
+ err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, 0, NULL,
+ &input);
+ assert(err == LY_SUCCESS);
+
+ for (size_t i = 0; i < vty->num_rpc_params; i++) {
+ err = lyd_new_path(input, ly_native_ctx,
+ vty->rpc_params[i].xpath,
+ vty->rpc_params[i].value, 0, NULL);
+ assert(err == LY_SUCCESS);
+ }
+
+ if (vty_mgmt_fe_enabled()) {
+ char *data = NULL;
+
+ err = lyd_print_mem(&data, input, LYD_JSON, LYD_PRINT_SHRINK);
+ assert(err == LY_SUCCESS);
+
+ ret = vty_mgmt_send_rpc_req(vty, LYD_JSON, xpath, data);
+
+ free(data);
+ lyd_free_all(input);
+
+ if (ret < 0)
+ return CMD_WARNING;
+ return CMD_SUCCESS;
+ }
+
+ /* validate input tree to create implicit defaults */
+ err = lyd_validate_op(input, NULL, LYD_TYPE_RPC_YANG, NULL);
+ assert(err == LY_SUCCESS);
+
+ /* create output tree root for population in the callback */
+ err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, 0, NULL,
+ &output);
+ assert(err == LY_SUCCESS);
+
ret = nb_callback_rpc(nb_node, xpath, input, output, errmsg,
sizeof(errmsg));
+
+ /* validate output tree to create implicit defaults */
+ err = lyd_validate_op(output, NULL, LYD_TYPE_REPLY_YANG, NULL);
+ assert(err == LY_SUCCESS);
+
+ lyd_free_all(input);
+ vty->num_rpc_params = 0;
+
switch (ret) {
case NB_OK:
+ if (output_p)
+ *output_p = output;
+ else
+ lyd_free_all(output);
return CMD_SUCCESS;
default:
+ lyd_free_all(output);
if (strlen(errmsg))
vty_show_nb_errors(vty, ret, errmsg);
return CMD_WARNING;
diff --git a/lib/northbound_cli.h b/lib/northbound_cli.h
index 1a45794d45..4c8dc50bd2 100644
--- a/lib/northbound_cli.h
+++ b/lib/northbound_cli.h
@@ -80,7 +80,23 @@ extern int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt,
...) PRINTFRR(2, 3);
/*
- * Execute a YANG RPC or Action.
+ * Add an input child node for an RPC or an action.
+ *
+ * vty
+ * The vty context.
+ *
+ * xpath
+ * XPath of the child being added, relative to the input container.
+ *
+ * value
+ * Value of the child being added. Can be NULL for containers and leafs of
+ * type 'empty'.
+ */
+extern int nb_cli_rpc_enqueue(struct vty *vty, const char *xpath,
+ const char *value);
+
+/*
+ * Execute a YANG RPC or Action using the enqueued input parameters.
*
* vty
* The vty terminal to dump any error.
@@ -88,20 +104,16 @@ extern int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt,
* xpath
* XPath of the YANG RPC or Action node.
*
- * input
- * List of 'yang_data' structures containing the RPC input parameters. It
- * can be set to NULL when there are no input parameters.
- *
- * output
- * List of 'yang_data' structures used to retrieve the RPC output parameters.
- * It can be set to NULL when it's known that the given YANG RPC or Action
- * doesn't have any output parameters.
+ * output_p
+ * A pointer to the libyang data node that will hold the output data tree.
+ * It can be set to NULL if the caller is not interested in processing the
+ * output. The caller is responsible for freeing the output data tree.
*
* Returns:
* CMD_SUCCESS on success, CMD_WARNING otherwise.
*/
-extern int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input,
- struct list *output);
+extern int nb_cli_rpc(struct vty *vty, const char *xpath,
+ struct lyd_node **output_p);
/*
* Show CLI commands associated to the given YANG data node.
diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp
index 7957752589..612ec3981b 100644
--- a/lib/northbound_grpc.cpp
+++ b/lib/northbound_grpc.cpp
@@ -1011,12 +1011,11 @@ grpc::Status HandleUnaryExecute(
grpc_debug("%s: entered", __func__);
struct nb_node *nb_node;
- struct list *input_list;
- struct list *output_list;
- struct listnode *node;
- struct yang_data *data;
+ struct lyd_node *input_tree, *output_tree, *child;
const char *xpath;
char errmsg[BUFSIZ] = {0};
+ char path[XPATH_MAXLEN];
+ LY_ERR err;
// Request: string path = 1;
xpath = tag->request.path().c_str();
@@ -1032,40 +1031,66 @@ grpc::Status HandleUnaryExecute(
return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
"Unknown data path");
- input_list = yang_data_list_new();
- output_list = yang_data_list_new();
+ // Create input data tree.
+ err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0,
+ (LYD_ANYDATA_VALUETYPE)0, 0, NULL, &input_tree);
+ if (err != LY_SUCCESS) {
+ return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
+ "Invalid data path");
+ }
// Read input parameters.
auto input = tag->request.input();
for (const frr::PathValue &pv : input) {
// Request: repeated PathValue input = 2;
- data = yang_data_new(pv.path().c_str(), pv.value().c_str());
- listnode_add(input_list, data);
+ err = lyd_new_path(input_tree, ly_native_ctx, pv.path().c_str(),
+ pv.value().c_str(), 0, NULL);
+ if (err != LY_SUCCESS) {
+ lyd_free_tree(input_tree);
+ return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
+ "Invalid input data");
+ }
+ }
+
+ // Validate input data.
+ err = lyd_validate_op(input_tree, NULL, LYD_TYPE_RPC_YANG, NULL);
+ if (err != LY_SUCCESS) {
+ lyd_free_tree(input_tree);
+ return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
+ "Invalid input data");
+ }
+
+ // Create output data tree.
+ err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0,
+ (LYD_ANYDATA_VALUETYPE)0, 0, NULL, &output_tree);
+ if (err != LY_SUCCESS) {
+ lyd_free_tree(input_tree);
+ return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
+ "Invalid data path");
}
// Execute callback registered for this XPath.
- if (nb_callback_rpc(nb_node, xpath, input_list, output_list, errmsg,
- sizeof(errmsg))
- != NB_OK) {
+ if (nb_callback_rpc(nb_node, xpath, input_tree, output_tree, errmsg,
+ sizeof(errmsg)) != NB_OK) {
flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s",
__func__, xpath);
- list_delete(&input_list);
- list_delete(&output_list);
+ lyd_free_tree(input_tree);
+ lyd_free_tree(output_tree);
return grpc::Status(grpc::StatusCode::INTERNAL, "RPC failed");
}
// Process output parameters.
- for (ALL_LIST_ELEMENTS_RO(output_list, node, data)) {
+ LY_LIST_FOR (lyd_child(output_tree), child) {
// Response: repeated PathValue output = 1;
frr::PathValue *pv = tag->response.add_output();
- pv->set_path(data->xpath);
- pv->set_value(data->value);
+ pv->set_path(lyd_path(child, LYD_PATH_STD, path, sizeof(path)));
+ pv->set_value(yang_dnode_get_string(child, NULL));
}
// Release memory.
- list_delete(&input_list);
- list_delete(&output_list);
+ lyd_free_tree(input_tree);
+ lyd_free_tree(output_tree);
return grpc::Status::OK;
}
diff --git a/lib/northbound_oper.c b/lib/northbound_oper.c
index 7e7190f5a4..5f38c970c7 100644
--- a/lib/northbound_oper.c
+++ b/lib/northbound_oper.c
@@ -1556,8 +1556,9 @@ static enum nb_error nb_op_yield(struct nb_op_yield_state *ys)
unsigned long min_us = MAX(1, NB_OP_WALK_INTERVAL_US / 50000);
struct timeval tv = { .tv_sec = 0, .tv_usec = min_us };
- DEBUGD(&nb_dbg_events, "NB oper-state: yielding %s for %lus (should_batch %d)",
- ys->xpath, tv.tv_usec, ys->should_batch);
+ DEBUGD(&nb_dbg_events,
+ "NB oper-state: yielding %s for %lldus (should_batch %d)",
+ ys->xpath, (long long)tv.tv_usec, ys->should_batch);
if (ys->should_batch) {
/*
diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c
index 640334926d..0ec7610a9a 100644
--- a/lib/northbound_sysrepo.c
+++ b/lib/northbound_sysrepo.c
@@ -377,16 +377,11 @@ static int frr_sr_state_cb(sr_session_ctx_t *session, uint32_t sub_id,
return SR_ERR_OK;
}
static int frr_sr_config_rpc_cb(sr_session_ctx_t *session, uint32_t sub_id,
- const char *xpath, const sr_val_t *sr_input,
- const size_t input_cnt, sr_event_t sr_ev,
- uint32_t request_id, sr_val_t **sr_output,
- size_t *sr_output_cnt, void *private_ctx)
+ const char *xpath, const struct lyd_node *input,
+ sr_event_t sr_ev, uint32_t request_id,
+ struct lyd_node *output, void *private_ctx)
{
struct nb_node *nb_node;
- struct list *input;
- struct list *output;
- struct yang_data *data;
- size_t cb_output_cnt;
int ret = SR_ERR_OK;
char errmsg[BUFSIZ] = {0};
@@ -397,19 +392,6 @@ static int frr_sr_config_rpc_cb(sr_session_ctx_t *session, uint32_t sub_id,
return SR_ERR_INTERNAL;
}
- input = yang_data_list_new();
- output = yang_data_list_new();
-
- /* Process input. */
- for (size_t i = 0; i < input_cnt; i++) {
- char value_str[YANG_VALUE_MAXLEN];
-
- sr_val_to_buff(&sr_input[i], value_str, sizeof(value_str));
-
- data = yang_data_new(xpath, value_str);
- listnode_add(input, data);
- }
-
/* Execute callback registered for this XPath. */
if (nb_callback_rpc(nb_node, xpath, input, output, errmsg,
sizeof(errmsg))
@@ -417,44 +399,8 @@ static int frr_sr_config_rpc_cb(sr_session_ctx_t *session, uint32_t sub_id,
flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s",
__func__, xpath);
ret = SR_ERR_OPERATION_FAILED;
- goto exit;
}
- /* Process output. */
- if (listcount(output) > 0) {
- sr_val_t *values = NULL;
- struct listnode *node;
- int i = 0;
-
- cb_output_cnt = listcount(output);
- ret = sr_new_values(cb_output_cnt, &values);
- if (ret != SR_ERR_OK) {
- flog_err(EC_LIB_LIBSYSREPO, "%s: sr_new_values(): %s",
- __func__, sr_strerror(ret));
- goto exit;
- }
-
- for (ALL_LIST_ELEMENTS_RO(output, node, data)) {
- if (yang_data_frr2sr(data, &values[i++]) != 0) {
- flog_err(
- EC_LIB_SYSREPO_DATA_CONVERT,
- "%s: failed to convert data to Sysrepo format",
- __func__);
- ret = SR_ERR_INTERNAL;
- sr_free_values(values, cb_output_cnt);
- goto exit;
- }
- }
-
- *sr_output = values;
- *sr_output_cnt = cb_output_cnt;
- }
-
-exit:
- /* Release memory. */
- list_delete(&input);
- list_delete(&output);
-
return ret;
}
@@ -579,8 +525,9 @@ static int frr_sr_subscribe_rpc(const struct lysc_node *snode, void *arg)
DEBUGD(&nb_dbg_client_sysrepo, "sysrepo: providing RPC to '%s'",
nb_node->xpath);
- ret = sr_rpc_subscribe(session, nb_node->xpath, frr_sr_config_rpc_cb,
- NULL, 0, 0, &module->sr_subscription);
+ ret = sr_rpc_subscribe_tree(session, nb_node->xpath,
+ frr_sr_config_rpc_cb, NULL, 0, 0,
+ &module->sr_subscription);
if (ret != SR_ERR_OK)
flog_err(EC_LIB_LIBSYSREPO, "sr_rpc_subscribe(): %s",
sr_strerror(ret));
diff --git a/lib/vty.c b/lib/vty.c
index d69b5eebd2..628c694e95 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -39,6 +39,7 @@
#include "libfrr.h"
#include "frrstr.h"
#include "lib_errors.h"
+#include <libyang/version.h>
#include "northbound_cli.h"
#include "printfrr.h"
#include "json.h"
@@ -3670,15 +3671,24 @@ static ssize_t vty_mgmt_libyang_print(void *user_data, const void *buf,
}
static void vty_out_yang_error(struct vty *vty, LYD_FORMAT format,
- struct ly_err_item *ei)
+ const struct ly_err_item *ei)
{
+#if (LY_VERSION_MAJOR < 3)
+#define data_path path
+#else
+#define data_path data_path
+#endif
bool have_apptag = ei->apptag && ei->apptag[0] != 0;
- bool have_path = ei->path && ei->path[0] != 0;
+ bool have_path = ei->data_path && ei->data_path[0] != 0;
bool have_msg = ei->msg && ei->msg[0] != 0;
const char *severity = NULL;
const char *evalid = NULL;
const char *ecode = NULL;
+#if (LY_VERSION_MAJOR < 3)
LY_ERR err = ei->no;
+#else
+ LY_ERR err = ei->err;
+#endif
if (ei->level == LY_LLERR)
severity = "error";
@@ -3703,7 +3713,8 @@ static void vty_out_yang_error(struct vty *vty, LYD_FORMAT format,
vty_out(vty, "<error-validation>%s</error-validation>\n",
evalid);
if (have_path)
- vty_out(vty, "<error-path>%s</error-path>\n", ei->path);
+ vty_out(vty, "<error-path>%s</error-path>\n",
+ ei->data_path);
if (have_apptag)
vty_out(vty, "<error-app-tag>%s</error-app-tag>\n",
ei->apptag);
@@ -3722,7 +3733,7 @@ static void vty_out_yang_error(struct vty *vty, LYD_FORMAT format,
if (evalid)
vty_out(vty, ", \"error-validation\": \"%s\"", evalid);
if (have_path)
- vty_out(vty, ", \"error-path\": \"%s\"", ei->path);
+ vty_out(vty, ", \"error-path\": \"%s\"", ei->data_path);
if (have_apptag)
vty_out(vty, ", \"error-app-tag\": \"%s\"", ei->apptag);
if (have_msg)
@@ -3739,18 +3750,19 @@ static void vty_out_yang_error(struct vty *vty, LYD_FORMAT format,
if (evalid)
vty_out(vty, " invalid: %s", evalid);
if (have_path)
- vty_out(vty, " path: %s", ei->path);
+ vty_out(vty, " path: %s", ei->data_path);
if (have_apptag)
vty_out(vty, " app-tag: %s", ei->apptag);
if (have_msg)
vty_out(vty, " msg: %s", ei->msg);
break;
}
+#undef data_path
}
static uint vty_out_yang_errors(struct vty *vty, LYD_FORMAT format)
{
- struct ly_err_item *ei = ly_err_first(ly_native_ctx);
+ const struct ly_err_item *ei = ly_err_first(ly_native_ctx);
uint count;
if (!ei)
@@ -3843,6 +3855,26 @@ static int vty_mgmt_edit_result_notified(struct mgmt_fe_client *client,
return 0;
}
+static int vty_mgmt_rpc_result_notified(struct mgmt_fe_client *client,
+ uintptr_t user_data, uint64_t client_id,
+ uint64_t session_id,
+ uintptr_t session_ctx, uint64_t req_id,
+ const char *result)
+{
+ struct vty *vty = (struct vty *)session_ctx;
+
+ debug_fe_client("RPC request for client 0x%" PRIx64 " req-id %" PRIu64
+ " was successful",
+ client_id, req_id);
+
+ if (result)
+ vty_out(vty, "%s\n", result);
+
+ vty_mgmt_resume_response(vty, CMD_SUCCESS);
+
+ return 0;
+}
+
static int vty_mgmt_error_notified(struct mgmt_fe_client *client,
uintptr_t user_data, uint64_t client_id,
uint64_t session_id, uintptr_t session_ctx,
@@ -3885,6 +3917,7 @@ static struct mgmt_fe_client_cbs mgmt_cbs = {
.get_data_notify = vty_mgmt_get_data_result_notified,
.get_tree_notify = vty_mgmt_get_tree_result_notified,
.edit_notify = vty_mgmt_edit_result_notified,
+ .rpc_notify = vty_mgmt_rpc_result_notified,
.error_notify = vty_mgmt_error_notified,
};
@@ -4162,6 +4195,25 @@ int vty_mgmt_send_edit_req(struct vty *vty, uint8_t datastore,
return 0;
}
+int vty_mgmt_send_rpc_req(struct vty *vty, LYD_FORMAT request_type,
+ const char *xpath, const char *data)
+{
+ vty->mgmt_req_id++;
+
+ if (mgmt_fe_send_rpc_req(mgmt_fe_client, vty->mgmt_session_id,
+ vty->mgmt_req_id, request_type, xpath, data)) {
+ zlog_err("Failed to send RPC to MGMTD session-id: %" PRIu64
+ " req-id %" PRIu64 ".",
+ vty->mgmt_session_id, vty->mgmt_req_id);
+ vty_out(vty, "Failed to send RPC to MGMTD!\n");
+ return -1;
+ }
+
+ vty->mgmt_req_pending_cmd = "MESSAGE_RPC_REQ";
+
+ return 0;
+}
+
/* Install vty's own commands like `who' command. */
void vty_init(struct event_loop *master_thread, bool do_command_logging)
{
diff --git a/lib/vty.h b/lib/vty.h
index 0e0c92c336..a9570ef048 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -122,6 +122,10 @@ struct vty {
size_t num_cfg_changes;
struct nb_cfg_change cfg_changes[VTY_MAXCFGCHANGES];
+ /* Input parameters */
+ size_t num_rpc_params;
+ struct nb_cfg_change rpc_params[VTY_MAXCFGCHANGES];
+
/* XPath of the current node */
int xpath_index;
char xpath[VTY_MAXDEPTH][XPATH_MAXLEN];
@@ -423,6 +427,8 @@ extern int vty_mgmt_send_edit_req(struct vty *vty, uint8_t datastore,
LYD_FORMAT request_type, uint8_t flags,
uint8_t operation, const char *xpath,
const char *data);
+extern int vty_mgmt_send_rpc_req(struct vty *vty, LYD_FORMAT request_type,
+ const char *xpath, const char *data);
extern int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id,
bool lock, bool scok);
extern void vty_mgmt_resume_response(struct vty *vty, int ret);
diff --git a/lib/yang.c b/lib/yang.c
index 013a762842..702fcf436d 100644
--- a/lib/yang.c
+++ b/lib/yang.c
@@ -11,6 +11,7 @@
#include "lib_errors.h"
#include "yang.h"
#include "yang_translator.h"
+#include <libyang/version.h>
#include "northbound.h"
#include "frrstr.h"
@@ -19,6 +20,17 @@
DEFINE_MTYPE_STATIC(LIB, YANG_MODULE, "YANG module");
DEFINE_MTYPE_STATIC(LIB, YANG_DATA, "YANG data structure");
+/* Safe to remove after libyang 2.2.8 */
+#if (LY_VERSION_MAJOR < 3)
+#define yang_lyd_find_xpath3(ctx_node, tree, xpath, format, prefix_data, vars, \
+ set) \
+ lyd_find_xpath3(ctx_node, tree, xpath, vars, set)
+#else
+#define yang_lyd_find_xpath3(ctx_node, tree, xpath, format, prefix_data, vars, \
+ set) \
+ lyd_find_xpath3(ctx_node, tree, xpath, LY_VALUE_JSON, NULL, vars, set)
+#endif
+
/* libyang container. */
struct ly_ctx *ly_native_ctx;
@@ -653,6 +665,16 @@ void yang_dnode_free(struct lyd_node *dnode)
lyd_free_all(dnode);
}
+void yang_dnode_rpc_output_add(struct lyd_node *output, const char *xpath,
+ const char *value)
+{
+ LY_ERR err;
+
+ err = lyd_new_path(output, ly_native_ctx, xpath, value,
+ LYD_NEW_PATH_OUTPUT | LYD_NEW_PATH_UPDATE, NULL);
+ assert(err == LY_SUCCESS);
+}
+
struct yang_data *yang_data_new(const char *xpath, const char *value)
{
struct yang_data *data;
@@ -702,7 +724,12 @@ struct yang_data *yang_data_list_find(const struct list *list,
}
/* Make libyang log its errors using FRR logging infrastructure. */
-static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path)
+static void ly_zlog_cb(LY_LOG_LEVEL level, const char *msg, const char *data_path
+#if !(LY_VERSION_MAJOR < 3)
+ ,
+ const char *schema_path, uint64_t line
+#endif
+)
{
int priority = LOG_ERR;
@@ -719,8 +746,14 @@ static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path)
break;
}
- if (path)
- zlog(priority, "libyang: %s (%s)", msg, path);
+ if (data_path)
+ zlog(priority, "libyang: %s (%s)", msg, data_path);
+#if !(LY_VERSION_MAJOR < 3)
+ else if (schema_path)
+ zlog(priority, "libyang %s (%s)\n", msg, schema_path);
+ else if (line)
+ zlog(priority, "libyang %s (line %" PRIu64 ")\n", msg, line);
+#endif
else
zlog(priority, "libyang: %s", msg);
}
@@ -747,7 +780,8 @@ LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format,
return err;
}
- err = lyd_find_xpath3(NULL, tree, xpath, NULL, &set);
+ err = yang_lyd_find_xpath3(NULL, tree, xpath, LY_VALUE_JSON, NULL, NULL,
+ &set);
if (err) {
zlog_err("Failed to parse notification: %s", ly_last_errmsg());
lyd_free_all(tree);
@@ -764,6 +798,63 @@ LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format,
return LY_SUCCESS;
}
+LY_ERR yang_parse_rpc(const char *xpath, LYD_FORMAT format, const char *data,
+ bool reply, struct lyd_node **rpc)
+{
+ const struct lysc_node *snode;
+ struct lyd_node *parent = NULL;
+ struct ly_in *in = NULL;
+ LY_ERR err;
+
+ snode = lys_find_path(ly_native_ctx, NULL, xpath, 0);
+ if (!snode) {
+ zlog_err("Failed to find RPC/action schema node: %s", xpath);
+ return LY_ENOTFOUND;
+ }
+
+ /* If it's an action, create its parent */
+ if (snode->nodetype == LYS_ACTION) {
+ char *parent_xpath = XSTRDUP(MTYPE_TMP, xpath);
+
+ if (yang_xpath_pop_node(parent_xpath) != NB_OK) {
+ XFREE(MTYPE_TMP, parent_xpath);
+ zlog_err("Invalid action xpath: %s", xpath);
+ return LY_EINVAL;
+ }
+
+ err = lyd_new_path2(NULL, ly_native_ctx, parent_xpath, NULL, 0,
+ 0, 0, NULL, &parent);
+ XFREE(MTYPE_TMP, parent_xpath);
+ if (err) {
+ zlog_err("Failed to create parent node for action: %s",
+ ly_last_errmsg());
+ return err;
+ }
+ } else if (snode->nodetype != LYS_RPC) {
+ zlog_err("Schema node is not an RPC/action: %s", xpath);
+ return LY_EINVAL;
+ }
+
+ err = ly_in_new_memory(data, &in);
+ if (err) {
+ lyd_free_all(parent);
+ zlog_err("Failed to initialize ly_in: %s", ly_last_errmsg());
+ return err;
+ }
+
+ err = lyd_parse_op(ly_native_ctx, parent, in, format,
+ reply ? LYD_TYPE_REPLY_YANG : LYD_TYPE_RPC_YANG,
+ NULL, rpc);
+ ly_in_free(in, 0);
+ if (err) {
+ lyd_free_all(parent);
+ zlog_err("Failed to parse RPC/action: %s", ly_last_errmsg());
+ return err;
+ }
+
+ return LY_SUCCESS;
+}
+
static ssize_t yang_print_darr(void *arg, const void *buf, size_t count)
{
uint8_t *dst = darr_append_n(*(uint8_t **)arg, count);
@@ -840,23 +931,29 @@ char *yang_convert_lyd_format(const char *data, size_t data_len,
const char *yang_print_errors(struct ly_ctx *ly_ctx, char *buf, size_t buf_len)
{
- struct ly_err_item *ei;
+ const struct ly_err_item *ei;
ei = ly_err_first(ly_ctx);
if (!ei)
return "";
strlcpy(buf, "YANG error(s):\n", buf_len);
+#if (LY_VERSION_MAJOR < 3)
+#define data_path path
+#else
+#define data_path data_path
+#endif
for (; ei; ei = ei->next) {
- if (ei->path) {
+ if (ei->data_path) {
strlcat(buf, " Path: ", buf_len);
- strlcat(buf, ei->path, buf_len);
+ strlcat(buf, ei->data_path, buf_len);
strlcat(buf, "\n", buf_len);
}
strlcat(buf, " Error: ", buf_len);
strlcat(buf, ei->msg, buf_len);
strlcat(buf, "\n", buf_len);
}
+#undef data_path
ly_err_clean(ly_ctx, NULL);
@@ -908,7 +1005,12 @@ struct ly_ctx *yang_ctx_new_setup(bool embedded_modules, bool explicit_compile)
void yang_init(bool embedded_modules, bool defer_compile)
{
/* Initialize libyang global parameters that affect all containers. */
- ly_set_log_clb(ly_log_cb, 1);
+ ly_set_log_clb(ly_zlog_cb
+#if (LY_VERSION_MAJOR < 3)
+ ,
+ 1
+#endif
+ );
ly_log_options(LY_LOLOG | LY_LOSTORE);
/* Initialize libyang container for native models. */
@@ -1246,7 +1348,8 @@ LY_ERR yang_lyd_trim_xpath(struct lyd_node **root, const char *xpath)
*root = lyd_first_sibling(*root);
- err = lyd_find_xpath3(NULL, *root, xpath, NULL, &set);
+ err = yang_lyd_find_xpath3(NULL, *root, xpath, LY_VALUE_JSON, NULL,
+ NULL, &set);
if (err) {
flog_err_sys(EC_LIB_LIBYANG,
"cannot obtain specific result for xpath \"%s\": %s",
diff --git a/lib/yang.h b/lib/yang.h
index 25703b1879..57131f478b 100644
--- a/lib/yang.h
+++ b/lib/yang.h
@@ -536,6 +536,21 @@ extern struct lyd_node *yang_dnode_dup(const struct lyd_node *dnode);
extern void yang_dnode_free(struct lyd_node *dnode);
/*
+ * Add a libyang data node to an RPC/action output container.
+ *
+ * output
+ * RPC/action output container.
+ *
+ * xpath
+ * XPath of the data node to add, relative to the output container.
+ *
+ * value
+ * String representing the value of the data node.
+ */
+extern void yang_dnode_rpc_output_add(struct lyd_node *output,
+ const char *xpath, const char *value);
+
+/*
* Create a new yang_data structure.
*
* xpath
@@ -620,6 +635,25 @@ extern LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format,
const char *data, struct lyd_node **notif);
/*
+ * Parse a YANG RPC.
+ *
+ * Args:
+ * xpath: xpath of an RPC/action.
+ * format: LYD_FORMAT of input data.
+ * data: input data.
+ * reply: true if the data represents a reply to an RPC/action.
+ * rpc: pointer to the libyang data tree to store the parsed RPC/action.
+ * If data represents an action, the pointer to the action node is
+ * still returned, but it's part of the full data tree with all its
+ * parents.
+ *
+ * Returns:
+ * LY_ERR from underlying calls.
+ */
+LY_ERR yang_parse_rpc(const char *xpath, LYD_FORMAT format, const char *data,
+ bool reply, struct lyd_node **rpc);
+
+/*
* "Print" the yang tree in `root` into dynamic sized array.
*
* Args:
diff --git a/mgmtd/mgmt_be_adapter.c b/mgmtd/mgmt_be_adapter.c
index 830c410676..fadeafa408 100644
--- a/mgmtd/mgmt_be_adapter.c
+++ b/mgmtd/mgmt_be_adapter.c
@@ -98,6 +98,10 @@ static const char *const ripd_oper_xpaths[] = {
"/ietf-key-chain:key-chains",
NULL,
};
+static const char *const ripd_rpc_xpaths[] = {
+ "/frr-ripd",
+ NULL,
+};
#endif
#if HAVE_RIPNGD
@@ -113,6 +117,10 @@ static const char *const ripngd_oper_xpaths[] = {
"/frr-ripngd:ripngd",
NULL,
};
+static const char *const ripngd_rpc_xpaths[] = {
+ "/frr-ripngd",
+ NULL,
+};
#endif
#if HAVE_STATICD
@@ -147,6 +155,15 @@ static const char *const *be_client_oper_xpaths[MGMTD_BE_CLIENT_ID_MAX] = {
[MGMTD_BE_CLIENT_ID_ZEBRA] = zebra_oper_xpaths,
};
+static const char *const *be_client_rpc_xpaths[MGMTD_BE_CLIENT_ID_MAX] = {
+#ifdef HAVE_RIPD
+ [MGMTD_BE_CLIENT_ID_RIPD] = ripd_rpc_xpaths,
+#endif
+#ifdef HAVE_RIPNGD
+ [MGMTD_BE_CLIENT_ID_RIPNGD] = ripngd_rpc_xpaths,
+#endif
+};
+
/*
* We would like to have a better ADT than one with O(n) comparisons
*
@@ -159,6 +176,7 @@ static const char *const *be_client_oper_xpaths[MGMTD_BE_CLIENT_ID_MAX] = {
static struct mgmt_be_xpath_map *be_cfg_xpath_map;
static struct mgmt_be_xpath_map *be_oper_xpath_map;
static struct mgmt_be_xpath_map *be_notif_xpath_map;
+static struct mgmt_be_xpath_map *be_rpc_xpath_map;
static struct event_loop *mgmt_loop;
static struct msg_server mgmt_be_server = {.fd = -1};
@@ -173,8 +191,8 @@ static struct mgmt_be_client_adapter
static void
mgmt_be_adapter_sched_init_event(struct mgmt_be_client_adapter *adapter);
-static bool be_is_client_interested(const char *xpath,
- enum mgmt_be_client_id id, bool config);
+static bool be_is_client_interested(const char *xpath, enum mgmt_be_client_id id,
+ enum mgmt_be_xpath_subscr_type type);
const char *mgmt_be_client_id2name(enum mgmt_be_client_id id)
{
@@ -223,16 +241,25 @@ mgmt_be_find_adapter_by_name(const char *name)
}
static void mgmt_register_client_xpath(enum mgmt_be_client_id id,
- const char *xpath, bool config, bool oper)
+ const char *xpath,
+ enum mgmt_be_xpath_subscr_type type)
{
struct mgmt_be_xpath_map **maps, *map;
- if (config)
+ switch (type) {
+ case MGMT_BE_XPATH_SUBSCR_TYPE_CFG:
maps = &be_cfg_xpath_map;
- else if (oper)
+ break;
+ case MGMT_BE_XPATH_SUBSCR_TYPE_OPER:
maps = &be_oper_xpath_map;
- else
+ break;
+ case MGMT_BE_XPATH_SUBSCR_TYPE_NOTIF:
maps = &be_notif_xpath_map;
+ break;
+ case MGMT_BE_XPATH_SUBSCR_TYPE_RPC:
+ maps = &be_rpc_xpath_map;
+ break;
+ }
darr_foreach_p (*maps, map) {
if (!strcmp(xpath, map->xpath_prefix)) {
@@ -260,18 +287,28 @@ static void mgmt_be_xpath_map_init(void)
/* Initialize the common config init map */
for (init = be_client_config_xpaths[id]; init && *init; init++) {
__dbg(" - CFG XPATH: '%s'", *init);
- mgmt_register_client_xpath(id, *init, true, false);
+ mgmt_register_client_xpath(id, *init,
+ MGMT_BE_XPATH_SUBSCR_TYPE_CFG);
}
/* Initialize the common oper init map */
for (init = be_client_oper_xpaths[id]; init && *init; init++) {
__dbg(" - OPER XPATH: '%s'", *init);
- mgmt_register_client_xpath(id, *init, false, true);
+ mgmt_register_client_xpath(id, *init,
+ MGMT_BE_XPATH_SUBSCR_TYPE_OPER);
+ }
+
+ /* Initialize the common RPC init map */
+ for (init = be_client_rpc_xpaths[id]; init && *init; init++) {
+ __dbg(" - RPC XPATH: '%s'", *init);
+ mgmt_register_client_xpath(id, *init,
+ MGMT_BE_XPATH_SUBSCR_TYPE_RPC);
}
}
__dbg("Total Cfg XPath Maps: %u", darr_len(be_cfg_xpath_map));
__dbg("Total Oper XPath Maps: %u", darr_len(be_oper_xpath_map));
+ __dbg("Total RPC XPath Maps: %u", darr_len(be_rpc_xpath_map));
}
static void mgmt_be_xpath_map_cleanup(void)
@@ -289,6 +326,10 @@ static void mgmt_be_xpath_map_cleanup(void)
darr_foreach_p (be_notif_xpath_map, map)
XFREE(MTYPE_MGMTD_XPATH, map->xpath_prefix);
darr_free(be_notif_xpath_map);
+
+ darr_foreach_p (be_rpc_xpath_map, map)
+ XFREE(MTYPE_MGMTD_XPATH, map->xpath_prefix);
+ darr_free(be_rpc_xpath_map);
}
@@ -405,11 +446,12 @@ mgmt_be_adapter_handle_msg(struct mgmt_be_client_adapter *adapter,
*/
switch ((int)be_msg->message_case) {
case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ:
- __dbg("Got SUBSCR_REQ from '%s' to register xpaths config: %zu oper: %zu notif: %zu",
+ __dbg("Got SUBSCR_REQ from '%s' to register xpaths config: %zu oper: %zu notif: %zu rpc: %zu",
be_msg->subscr_req->client_name,
be_msg->subscr_req->n_config_xpaths,
be_msg->subscr_req->n_oper_xpaths,
- be_msg->subscr_req->n_notif_xpaths);
+ be_msg->subscr_req->n_notif_xpaths,
+ be_msg->subscr_req->n_rpc_xpaths);
if (strlen(be_msg->subscr_req->client_name)) {
strlcpy(adapter->name, be_msg->subscr_req->client_name,
@@ -432,22 +474,29 @@ mgmt_be_adapter_handle_msg(struct mgmt_be_client_adapter *adapter,
num = be_msg->subscr_req->n_config_xpaths;
for (i = 0; i < num; i++) {
xpath = be_msg->subscr_req->config_xpaths[i];
- mgmt_register_client_xpath(adapter->id, xpath, true,
- false);
+ mgmt_register_client_xpath(adapter->id, xpath,
+ MGMT_BE_XPATH_SUBSCR_TYPE_CFG);
}
num = be_msg->subscr_req->n_oper_xpaths;
for (i = 0; i < num; i++) {
xpath = be_msg->subscr_req->oper_xpaths[i];
- mgmt_register_client_xpath(adapter->id, xpath, false,
- true);
+ mgmt_register_client_xpath(adapter->id, xpath,
+ MGMT_BE_XPATH_SUBSCR_TYPE_OPER);
}
num = be_msg->subscr_req->n_notif_xpaths;
for (i = 0; i < num; i++) {
xpath = be_msg->subscr_req->notif_xpaths[i];
- mgmt_register_client_xpath(adapter->id, xpath, false,
- false);
+ mgmt_register_client_xpath(adapter->id, xpath,
+ MGMT_BE_XPATH_SUBSCR_TYPE_NOTIF);
+ }
+
+ num = be_msg->subscr_req->n_rpc_xpaths;
+ for (i = 0; i < num; i++) {
+ xpath = be_msg->subscr_req->rpc_xpaths[i];
+ mgmt_register_client_xpath(adapter->id, xpath,
+ MGMT_BE_XPATH_SUBSCR_TYPE_RPC);
}
mgmt_be_send_subscr_reply(adapter, true);
@@ -638,6 +687,7 @@ static void be_adapter_handle_native_msg(struct mgmt_be_client_adapter *adapter,
{
struct mgmt_msg_notify_data *notify_msg;
struct mgmt_msg_tree_data *tree_msg;
+ struct mgmt_msg_rpc_reply *rpc_msg;
struct mgmt_msg_error *error_msg;
/* get the transaction */
@@ -662,6 +712,15 @@ static void be_adapter_handle_native_msg(struct mgmt_be_client_adapter *adapter,
/* Forward the reply to the txn module */
mgmt_txn_notify_tree_data_reply(adapter, tree_msg, msg_len);
break;
+ case MGMT_MSG_CODE_RPC_REPLY:
+ /* RPC reply from a backend client */
+ rpc_msg = (typeof(rpc_msg))msg;
+ __dbg("Got RPC_REPLY from '%s' txn-id %" PRIx64, adapter->name,
+ msg->refer_id);
+
+ /* Forward the reply to the txn module */
+ mgmt_txn_notify_rpc_reply(adapter, rpc_msg, msg_len);
+ break;
case MGMT_MSG_CODE_NOTIFY:
notify_msg = (typeof(notify_msg))msg;
__dbg("Got NOTIFY from '%s'", adapter->name);
@@ -882,7 +941,8 @@ void mgmt_be_get_adapter_config(struct mgmt_be_client_adapter *adapter,
goto walk_cont;
xpath = lyd_path(dnode, LYD_PATH_STD, NULL, 0);
- if (be_is_client_interested(xpath, adapter->id, true))
+ if (be_is_client_interested(xpath, adapter->id,
+ MGMT_BE_XPATH_SUBSCR_TYPE_CFG))
nb_config_diff_add_change(*changes, NB_CB_CREATE, &seq, dnode);
else
LYD_TREE_DFS_continue = 1; /* skip any subtree */
@@ -893,13 +953,27 @@ void mgmt_be_get_adapter_config(struct mgmt_be_client_adapter *adapter,
}
}
-uint64_t mgmt_be_interested_clients(const char *xpath, bool config)
+uint64_t mgmt_be_interested_clients(const char *xpath,
+ enum mgmt_be_xpath_subscr_type type)
{
- struct mgmt_be_xpath_map *maps, *map;
+ struct mgmt_be_xpath_map *maps = NULL, *map;
enum mgmt_be_client_id id;
uint64_t clients;
- maps = config ? be_cfg_xpath_map : be_oper_xpath_map;
+ switch (type) {
+ case MGMT_BE_XPATH_SUBSCR_TYPE_CFG:
+ maps = be_cfg_xpath_map;
+ break;
+ case MGMT_BE_XPATH_SUBSCR_TYPE_OPER:
+ maps = be_oper_xpath_map;
+ break;
+ case MGMT_BE_XPATH_SUBSCR_TYPE_NOTIF:
+ maps = be_notif_xpath_map;
+ break;
+ case MGMT_BE_XPATH_SUBSCR_TYPE_RPC:
+ maps = be_rpc_xpath_map;
+ break;
+ }
clients = 0;
@@ -928,8 +1002,8 @@ uint64_t mgmt_be_interested_clients(const char *xpath, bool config)
* Returns:
* Interested or not.
*/
-static bool be_is_client_interested(const char *xpath,
- enum mgmt_be_client_id id, bool config)
+static bool be_is_client_interested(const char *xpath, enum mgmt_be_client_id id,
+ enum mgmt_be_xpath_subscr_type type)
{
uint64_t clients;
@@ -938,7 +1012,7 @@ static bool be_is_client_interested(const char *xpath,
__dbg("Checking client: %s for xpath: '%s'", mgmt_be_client_id2name(id),
xpath);
- clients = mgmt_be_interested_clients(xpath, config);
+ clients = mgmt_be_interested_clients(xpath, type);
if (IS_IDBIT_SET(clients, id)) {
__dbg("client: %s: interested", mgmt_be_client_id2name(id));
return true;
@@ -998,23 +1072,32 @@ void mgmt_be_xpath_register_write(struct vty *vty)
darr_len(be_oper_xpath_map));
darr_foreach_p (be_oper_xpath_map, map)
be_show_xpath_register(vty, map);
+
+ vty_out(vty, "\nMGMTD Backend RPC XPath Registry: Count: %u\n",
+ darr_len(be_rpc_xpath_map));
+ darr_foreach_p (be_rpc_xpath_map, map)
+ be_show_xpath_register(vty, map);
}
void mgmt_be_show_xpath_registries(struct vty *vty, const char *xpath)
{
enum mgmt_be_client_id id;
struct mgmt_be_client_adapter *adapter;
- uint64_t cclients, oclients, combined;
-
- cclients = mgmt_be_interested_clients(xpath, true);
- oclients = mgmt_be_interested_clients(xpath, false);
+ uint64_t cclients, oclients, rclients, combined;
+
+ cclients = mgmt_be_interested_clients(xpath,
+ MGMT_BE_XPATH_SUBSCR_TYPE_CFG);
+ oclients = mgmt_be_interested_clients(xpath,
+ MGMT_BE_XPATH_SUBSCR_TYPE_OPER);
+ rclients = mgmt_be_interested_clients(xpath,
+ MGMT_BE_XPATH_SUBSCR_TYPE_RPC);
combined = cclients | oclients;
vty_out(vty, "XPath: '%s'\n", xpath);
FOREACH_BE_CLIENT_BITS (id, combined) {
- vty_out(vty, " -- Client: '%s'\tconfig:%d oper:%d\n",
+ vty_out(vty, " -- Client: '%s'\tconfig:%d oper:%d rpc:%d\n",
mgmt_be_client_id2name(id), IS_IDBIT_SET(cclients, id),
- IS_IDBIT_SET(oclients, id));
+ IS_IDBIT_SET(oclients, id), IS_IDBIT_SET(rclients, id));
adapter = mgmt_be_get_adapter_by_id(id);
if (adapter)
vty_out(vty, " -- Adapter: %p\n", adapter);
diff --git a/mgmtd/mgmt_be_adapter.h b/mgmtd/mgmt_be_adapter.h
index 491410aa15..c9f2ab1b7a 100644
--- a/mgmtd/mgmt_be_adapter.h
+++ b/mgmtd/mgmt_be_adapter.h
@@ -235,15 +235,23 @@ extern void mgmt_be_xpath_register_write(struct vty *vty);
*/
extern int mgmt_be_send_native(enum mgmt_be_client_id id, void *msg);
+enum mgmt_be_xpath_subscr_type {
+ MGMT_BE_XPATH_SUBSCR_TYPE_CFG,
+ MGMT_BE_XPATH_SUBSCR_TYPE_OPER,
+ MGMT_BE_XPATH_SUBSCR_TYPE_NOTIF,
+ MGMT_BE_XPATH_SUBSCR_TYPE_RPC,
+};
+
/**
* Lookup the clients which are subscribed to a given `xpath`
* and the way they are subscribed.
*
* Args:
* xpath - the xpath to check for subscription information.
- * config - true for config interest false for oper interest.
+ * type - type of subscription to check for.
*/
-extern uint64_t mgmt_be_interested_clients(const char *xpath, bool config);
+extern uint64_t mgmt_be_interested_clients(const char *xpath,
+ enum mgmt_be_xpath_subscr_type type);
/**
* mgmt_fe_adapter_send_notify() - notify FE clients of a notification.
diff --git a/mgmtd/mgmt_be_nb.c b/mgmtd/mgmt_be_nb.c
new file mode 100644
index 0000000000..613272d407
--- /dev/null
+++ b/mgmtd/mgmt_be_nb.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "config.h"
+#include "xref.h"
+
+XREF_SETUP();
diff --git a/mgmtd/mgmt_fe_adapter.c b/mgmtd/mgmt_fe_adapter.c
index ab0da64d8f..fc1bde0b38 100644
--- a/mgmtd/mgmt_fe_adapter.c
+++ b/mgmtd/mgmt_fe_adapter.c
@@ -1101,6 +1101,47 @@ done:
return ret;
}
+static int fe_adapter_send_rpc_reply(struct mgmt_fe_session_ctx *session,
+ uint64_t req_id, uint8_t result_type,
+ const struct lyd_node *result)
+{
+ struct mgmt_msg_rpc_reply *msg;
+ uint8_t **darrp = NULL;
+ int ret;
+
+ msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_rpc_reply, 0,
+ MTYPE_MSG_NATIVE_RPC_REPLY);
+ msg->refer_id = session->session_id;
+ msg->req_id = req_id;
+ msg->code = MGMT_MSG_CODE_RPC_REPLY;
+ msg->result_type = result_type;
+
+ if (result) {
+ darrp = mgmt_msg_native_get_darrp(msg);
+ ret = yang_print_tree_append(darrp, result, result_type, 0);
+ if (ret != LY_SUCCESS) {
+ __log_err("Error building rpc-reply result for client %s session-id %" PRIu64
+ " req-id %" PRIu64 " result type %u",
+ session->adapter->name, session->session_id,
+ req_id, result_type);
+ goto done;
+ }
+ }
+
+ __dbg("Sending rpc-reply from adapter %s to session-id %" PRIu64
+ " req-id %" PRIu64 " len %u",
+ session->adapter->name, session->session_id, req_id,
+ mgmt_msg_native_get_msg_len(msg));
+
+ ret = fe_adapter_send_native_msg(session->adapter, msg,
+ mgmt_msg_native_get_msg_len(msg),
+ false);
+done:
+ mgmt_msg_native_free_msg(msg);
+
+ return ret;
+}
+
static int fe_adapter_send_edit_reply(struct mgmt_fe_session_ctx *session,
uint64_t req_id, const char *xpath)
{
@@ -1215,7 +1256,8 @@ static void fe_adapter_handle_get_data(struct mgmt_fe_session_ctx *session,
}
darr_free(snodes);
- clients = mgmt_be_interested_clients(msg->xpath, false);
+ clients = mgmt_be_interested_clients(msg->xpath,
+ MGMT_BE_XPATH_SUBSCR_TYPE_OPER);
if (!clients && !CHECK_FLAG(msg->flags, GET_DATA_FLAG_CONFIG)) {
__dbg("No backends provide xpath: %s for txn-id: %" PRIu64
" session-id: %" PRIu64,
@@ -1270,7 +1312,7 @@ static void fe_adapter_handle_edit(struct mgmt_fe_session_ctx *session,
}
xpath = mgmt_msg_native_xpath_data_decode(msg, msg_len, data);
- if (!xpath || !data) {
+ if (!xpath) {
fe_adapter_send_error(session, msg->req_id, false, -EINVAL,
"Invalid message");
return;
@@ -1360,6 +1402,90 @@ static void fe_adapter_handle_edit(struct mgmt_fe_session_ctx *session,
}
/**
+ * fe_adapter_handle_rpc() - Handle an RPC message from an FE client.
+ * @session: the client session.
+ * @msg_raw: the message data.
+ * @msg_len: the length of the message data.
+ */
+static void fe_adapter_handle_rpc(struct mgmt_fe_session_ctx *session,
+ void *__msg, size_t msg_len)
+{
+ struct mgmt_msg_rpc *msg = __msg;
+ const struct lysc_node *snode;
+ const char *xpath, *data;
+ uint64_t req_id = msg->req_id;
+ uint64_t clients;
+ int ret;
+
+ __dbg("Received RPC request from client %s for session-id %" PRIu64
+ " req-id %" PRIu64,
+ session->adapter->name, session->session_id, msg->req_id);
+
+ xpath = mgmt_msg_native_xpath_data_decode(msg, msg_len, data);
+ if (!xpath) {
+ fe_adapter_send_error(session, req_id, false, -EINVAL,
+ "Invalid message");
+ return;
+ }
+
+ if (session->txn_id != MGMTD_TXN_ID_NONE) {
+ fe_adapter_send_error(session, req_id, false, -EINPROGRESS,
+ "Transaction in progress txn-id: %" PRIu64
+ " for session-id: %" PRIu64,
+ session->txn_id, session->session_id);
+ return;
+ }
+
+ snode = lys_find_path(ly_native_ctx, NULL, xpath, 0);
+ if (!snode) {
+ fe_adapter_send_error(session, req_id, false, -ENOENT,
+ "No such path: %s", xpath);
+ return;
+ }
+
+ if (snode->nodetype != LYS_RPC && snode->nodetype != LYS_ACTION) {
+ fe_adapter_send_error(session, req_id, false, -EINVAL,
+ "Not an RPC or action path: %s", xpath);
+ return;
+ }
+
+ clients = mgmt_be_interested_clients(xpath,
+ MGMT_BE_XPATH_SUBSCR_TYPE_RPC);
+ if (!clients) {
+ __dbg("No backends implement xpath: %s for txn-id: %" PRIu64
+ " session-id: %" PRIu64,
+ xpath, session->txn_id, session->session_id);
+
+ fe_adapter_send_error(session, req_id, false, -ENOENT,
+ "No backends implement xpath: %s", xpath);
+ return;
+ }
+
+ /* Start a RPC Transaction */
+ session->txn_id = mgmt_create_txn(session->session_id,
+ MGMTD_TXN_TYPE_RPC);
+ if (session->txn_id == MGMTD_SESSION_ID_NONE) {
+ fe_adapter_send_error(session, req_id, false, -EINPROGRESS,
+ "Failed to create an RPC transaction");
+ return;
+ }
+
+ __dbg("Created new rpc txn-id: %" PRIu64 " for session-id: %" PRIu64,
+ session->txn_id, session->session_id);
+
+ /* Create an RPC request under the transaction */
+ ret = mgmt_txn_send_rpc(session->txn_id, req_id, clients,
+ msg->request_type, xpath, data,
+ mgmt_msg_native_data_len_decode(msg, msg_len));
+ if (ret) {
+ /* destroy the just created txn */
+ mgmt_destroy_txn(&session->txn_id);
+ fe_adapter_send_error(session, req_id, false, -EINPROGRESS,
+ "Failed to create an RPC transaction");
+ }
+}
+
+/**
* Handle a native encoded message from the FE client.
*/
static void fe_adapter_handle_native_msg(struct mgmt_fe_client_adapter *adapter,
@@ -1383,6 +1509,9 @@ static void fe_adapter_handle_native_msg(struct mgmt_fe_client_adapter *adapter,
case MGMT_MSG_CODE_EDIT:
fe_adapter_handle_edit(session, msg, msg_len);
break;
+ case MGMT_MSG_CODE_RPC:
+ fe_adapter_handle_rpc(session, msg, msg_len);
+ break;
default:
__log_err("unknown native message session-id %" PRIu64
" req-id %" PRIu64 " code %u to FE adapter %s",
@@ -1622,6 +1751,24 @@ int mgmt_fe_adapter_send_tree_data(uint64_t session_id, uint64_t txn_id,
return ret;
}
+int mgmt_fe_adapter_send_rpc_reply(uint64_t session_id, uint64_t txn_id,
+ uint64_t req_id, LYD_FORMAT result_type,
+ const struct lyd_node *result)
+{
+ struct mgmt_fe_session_ctx *session;
+ int ret;
+
+ session = mgmt_session_id2ctx(session_id);
+ if (!session || session->txn_id != txn_id)
+ return -1;
+
+ ret = fe_adapter_send_rpc_reply(session, req_id, result_type, result);
+
+ mgmt_destroy_txn(&session->txn_id);
+
+ return ret;
+}
+
int mgmt_fe_adapter_send_edit_reply(uint64_t session_id, uint64_t txn_id,
uint64_t req_id, bool unlock, bool commit,
const char *xpath, int16_t error,
diff --git a/mgmtd/mgmt_fe_adapter.h b/mgmtd/mgmt_fe_adapter.h
index 8d61ffe910..61d6cfae13 100644
--- a/mgmtd/mgmt_fe_adapter.h
+++ b/mgmtd/mgmt_fe_adapter.h
@@ -163,6 +163,26 @@ mgmt_fe_adapter_send_tree_data(uint64_t session_id, uint64_t txn_id,
int partial_error, bool short_circuit_ok);
/**
+ * Send RPC reply back to client.
+ *
+ * This also cleans up and frees the transaction.
+ *
+ * Args:
+ * session_id: the session.
+ * txn_id: the txn_id this data pertains to
+ * req_id: the req id for the rpc message
+ * result_type: the format of the result data.
+ * result: the results.
+ *
+ * Return:
+ * the return value from the underlying send function.
+ */
+extern int mgmt_fe_adapter_send_rpc_reply(uint64_t session_id, uint64_t txn_id,
+ uint64_t req_id,
+ LYD_FORMAT result_type,
+ const struct lyd_node *result);
+
+/**
* Send edit reply back to client. If error is not 0, a native error is sent.
*
* This also cleans up and frees the transaction.
diff --git a/mgmtd/mgmt_memory.c b/mgmtd/mgmt_memory.c
index 0fce61aa97..72ccca0626 100644
--- a/mgmtd/mgmt_memory.c
+++ b/mgmtd/mgmt_memory.c
@@ -20,6 +20,7 @@
DEFINE_MGROUP(MGMTD, "mgmt");
DEFINE_MTYPE(MGMTD, MGMTD, "instance");
DEFINE_MTYPE(MGMTD, MGMTD_XPATH, "xpath regex");
+DEFINE_MTYPE(MGMTD, MGMTD_ERR, "error");
DEFINE_MTYPE(MGMTD, MGMTD_BE_ADPATER, "backend adapter");
DEFINE_MTYPE(MGMTD, MGMTD_FE_ADPATER, "frontend adapter");
DEFINE_MTYPE(MGMTD, MGMTD_FE_SESSION, "frontend session");
@@ -30,5 +31,6 @@ DEFINE_MTYPE(MGMTD, MGMTD_TXN_COMMCFG_REQ, "txn commit-config requests");
DEFINE_MTYPE(MGMTD, MGMTD_TXN_GETDATA_REQ, "txn get-data requests");
DEFINE_MTYPE(MGMTD, MGMTD_TXN_GETDATA_REPLY, "txn get-data replies");
DEFINE_MTYPE(MGMTD, MGMTD_TXN_GETTREE_REQ, "txn get-tree requests");
+DEFINE_MTYPE(MGMTD, MGMTD_TXN_RPC_REQ, "txn rpc requests");
DEFINE_MTYPE(MGMTD, MGMTD_TXN_CFG_BATCH, "txn config batches");
DEFINE_MTYPE(MGMTD, MGMTD_CMT_INFO, "commit info");
diff --git a/mgmtd/mgmt_memory.h b/mgmtd/mgmt_memory.h
index d5b6aa632e..e28586ed83 100644
--- a/mgmtd/mgmt_memory.h
+++ b/mgmtd/mgmt_memory.h
@@ -14,6 +14,7 @@
DECLARE_MGROUP(MGMTD);
DECLARE_MTYPE(MGMTD);
DECLARE_MTYPE(MGMTD_XPATH);
+DECLARE_MTYPE(MGMTD_ERR);
DECLARE_MTYPE(MGMTD_BE_ADPATER);
DECLARE_MTYPE(MGMTD_FE_ADPATER);
DECLARE_MTYPE(MGMTD_FE_SESSION);
@@ -24,6 +25,7 @@ DECLARE_MTYPE(MGMTD_TXN_COMMCFG_REQ);
DECLARE_MTYPE(MGMTD_TXN_GETDATA_REQ);
DECLARE_MTYPE(MGMTD_TXN_GETDATA_REPLY);
DECLARE_MTYPE(MGMTD_TXN_GETTREE_REQ);
+DECLARE_MTYPE(MGMTD_TXN_RPC_REQ);
DECLARE_MTYPE(MGMTD_TXN_CFG_BATCH);
DECLARE_MTYPE(MGMTD_BE_ADAPTER_MSG_BUF);
DECLARE_MTYPE(MGMTD_CMT_INFO);
diff --git a/mgmtd/mgmt_testc.c b/mgmtd/mgmt_testc.c
index a33a55efca..8bb07ed068 100644
--- a/mgmtd/mgmt_testc.c
+++ b/mgmtd/mgmt_testc.c
@@ -18,6 +18,7 @@
/* ---------------- */
static void async_notification(struct nb_cb_notify_args *args);
+static int rpc_callback(struct nb_cb_rpc_args *args);
static void sigusr1(void);
static void sigint(void);
@@ -87,6 +88,10 @@ static const struct frr_yang_module_info frr_ripd_info = {
.cbs.notify = async_notification,
},
{
+ .xpath = "/frr-ripd:clear-rip-route",
+ .cbs.rpc = rpc_callback,
+ },
+ {
.xpath = NULL,
}
}
@@ -113,6 +118,7 @@ FRR_DAEMON_INFO(mgmtd_testc, MGMTD_TESTC,
/* clang-format on */
const char **__notif_xpaths;
+const char **__rpc_xpaths;
struct mgmt_be_client_cbs __client_cbs = {};
struct event *event_timeout;
@@ -134,6 +140,7 @@ static void quit(int exit_code)
{
EVENT_OFF(event_timeout);
darr_free(__client_cbs.notif_xpaths);
+ darr_free(__client_cbs.rpc_xpaths);
frr_fini();
@@ -152,6 +159,12 @@ static void timeout(struct event *event)
quit(1);
}
+static void success(struct event *event)
+{
+ zlog_notice("Success, exiting");
+ quit(0);
+}
+
static void async_notification(struct nb_cb_notify_args *args)
{
zlog_notice("Received YANG notification");
@@ -163,6 +176,23 @@ static void async_notification(struct nb_cb_notify_args *args)
quit(0);
}
+static int rpc_callback(struct nb_cb_rpc_args *args)
+{
+ const char *vrf = NULL;
+
+ zlog_notice("Received YANG RPC");
+
+ if (yang_dnode_exists(args->input, "vrf"))
+ vrf = yang_dnode_get_string(args->input, "vrf");
+
+ printf("{\"frr-ripd:clear-rip-route\": {\"vrf\": \"%s\"}}\n", vrf);
+
+ event_cancel(&event_timeout);
+ event_add_timer(master, success, NULL, 1, NULL);
+
+ return 0;
+}
+
int main(int argc, char **argv)
{
int f_listen = 0;
@@ -217,6 +247,10 @@ int main(int argc, char **argv)
__client_cbs.nnotif_xpaths = darr_len(__notif_xpaths);
}
+ darr_push(__rpc_xpaths, "/frr-ripd:clear-rip-route");
+ __client_cbs.rpc_xpaths = __rpc_xpaths;
+ __client_cbs.nrpc_xpaths = darr_len(__rpc_xpaths);
+
mgmt_be_client = mgmt_be_client_create("mgmtd-testc", &__client_cbs, 0,
master);
diff --git a/mgmtd/mgmt_txn.c b/mgmtd/mgmt_txn.c
index 901163c6e6..0a80b3bbf7 100644
--- a/mgmtd/mgmt_txn.c
+++ b/mgmtd/mgmt_txn.c
@@ -29,6 +29,7 @@ enum mgmt_txn_event {
MGMTD_TXN_PROC_COMMITCFG,
MGMTD_TXN_PROC_GETCFG,
MGMTD_TXN_PROC_GETTREE,
+ MGMTD_TXN_PROC_RPC,
MGMTD_TXN_COMMITCFG_TIMEOUT,
};
@@ -188,6 +189,15 @@ struct txn_req_get_tree {
struct lyd_node *client_results; /* result tree from clients */
};
+struct txn_req_rpc {
+ char *xpath; /* xpath of rpc/action to invoke */
+ uint64_t sent_clients; /* Bitmask of clients sent req to */
+ uint64_t recv_clients; /* Bitmask of clients recv reply from */
+ uint8_t result_type; /* LYD_FORMAT for results */
+ char *errstr; /* error string */
+ struct lyd_node *client_results; /* result tree from clients */
+};
+
struct mgmt_txn_req {
struct mgmt_txn_ctx *txn;
enum mgmt_txn_event req_event;
@@ -196,6 +206,7 @@ struct mgmt_txn_req {
struct mgmt_set_cfg_req *set_cfg;
struct mgmt_get_data_req *get_data;
struct txn_req_get_tree *get_tree;
+ struct txn_req_rpc *rpc;
struct mgmt_commit_cfg_req commit_cfg;
} req;
@@ -221,6 +232,7 @@ struct mgmt_txn_ctx {
struct event *proc_get_tree;
struct event *comm_cfg_timeout;
struct event *get_tree_timeout;
+ struct event *rpc_timeout;
struct event *clnup;
/* List of backend adapters involved in this transaction */
@@ -253,6 +265,10 @@ struct mgmt_txn_ctx {
*/
struct mgmt_txn_reqs_head get_tree_reqs;
/*
+ * List of pending rpc requests.
+ */
+ struct mgmt_txn_reqs_head rpc_reqs;
+ /*
* There will always be one commit-config allowed for a given
* transaction/session. No need to maintain lists for it.
*/
@@ -416,6 +432,15 @@ static struct mgmt_txn_req *mgmt_txn_req_alloc(struct mgmt_txn_ctx *txn,
" session-id: %" PRIu64,
txn_req->req_id, txn->txn_id, txn->session_id);
break;
+ case MGMTD_TXN_PROC_RPC:
+ txn_req->req.rpc = XCALLOC(MTYPE_MGMTD_TXN_RPC_REQ,
+ sizeof(struct txn_req_rpc));
+ assert(txn_req->req.rpc);
+ mgmt_txn_reqs_add_tail(&txn->rpc_reqs, txn_req);
+ __dbg("Added a new RPC req-id: %" PRIu64 " txn-id: %" PRIu64
+ " session-id: %" PRIu64,
+ txn_req->req_id, txn->txn_id, txn->session_id);
+ break;
case MGMTD_TXN_COMMITCFG_TIMEOUT:
break;
}
@@ -506,6 +531,15 @@ static void mgmt_txn_req_free(struct mgmt_txn_req **txn_req)
XFREE(MTYPE_MGMTD_XPATH, (*txn_req)->req.get_tree->xpath);
XFREE(MTYPE_MGMTD_TXN_GETTREE_REQ, (*txn_req)->req.get_tree);
break;
+ case MGMTD_TXN_PROC_RPC:
+ __dbg("Deleting RPC req-id: %" PRIu64 " txn-id: %" PRIu64,
+ (*txn_req)->req_id, (*txn_req)->txn->txn_id);
+ req_list = &(*txn_req)->txn->rpc_reqs;
+ lyd_free_all((*txn_req)->req.rpc->client_results);
+ XFREE(MTYPE_MGMTD_ERR, (*txn_req)->req.rpc->errstr);
+ XFREE(MTYPE_MGMTD_XPATH, (*txn_req)->req.rpc->xpath);
+ XFREE(MTYPE_MGMTD_TXN_RPC_REQ, (*txn_req)->req.rpc);
+ break;
case MGMTD_TXN_COMMITCFG_TIMEOUT:
break;
}
@@ -880,7 +914,9 @@ static int mgmt_txn_create_config_batches(struct mgmt_txn_req *txn_req,
__dbg("XPATH: %s, Value: '%s'", xpath, value ? value : "NIL");
- clients = mgmt_be_interested_clients(xpath, true);
+ clients =
+ mgmt_be_interested_clients(xpath,
+ MGMT_BE_XPATH_SUBSCR_TYPE_CFG);
chg_clients = 0;
@@ -1306,6 +1342,33 @@ static int txn_get_tree_data_done(struct mgmt_txn_ctx *txn,
return ret;
}
+static int txn_rpc_done(struct mgmt_txn_ctx *txn, struct mgmt_txn_req *txn_req)
+{
+ struct txn_req_rpc *rpc = txn_req->req.rpc;
+ uint64_t req_id = txn_req->req_id;
+
+ /* cancel timer and send reply onward */
+ EVENT_OFF(txn->rpc_timeout);
+
+ if (rpc->errstr)
+ mgmt_fe_adapter_txn_error(txn->txn_id, req_id, false, -1,
+ rpc->errstr);
+ else if (mgmt_fe_adapter_send_rpc_reply(txn->session_id, txn->txn_id,
+ req_id, rpc->result_type,
+ rpc->client_results)) {
+ __log_err("Error sending the results of RPC for txn-id %" PRIu64
+ " req_id %" PRIu64 " to requested type %u",
+ txn->txn_id, req_id, rpc->result_type);
+
+ (void)mgmt_fe_adapter_txn_error(txn->txn_id, req_id, false, -1,
+ "Error converting results of RPC");
+ }
+
+ /* we're done with the request */
+ mgmt_txn_req_free(&txn_req);
+
+ return 0;
+}
static void txn_get_tree_timeout(struct event *thread)
{
@@ -1333,6 +1396,31 @@ static void txn_get_tree_timeout(struct event *thread)
txn_get_tree_data_done(txn, txn_req);
}
+static void txn_rpc_timeout(struct event *thread)
+{
+ struct mgmt_txn_ctx *txn;
+ struct mgmt_txn_req *txn_req;
+
+ txn_req = (struct mgmt_txn_req *)EVENT_ARG(thread);
+ txn = txn_req->txn;
+
+ assert(txn);
+ assert(txn->type == MGMTD_TXN_TYPE_RPC);
+
+ __log_err("Backend timeout txn-id: %" PRIu64 " ending rpc", txn->txn_id);
+
+ /*
+ * Send a get-tree data reply.
+ *
+ * NOTE: The transaction cleanup will be triggered from Front-end
+ * adapter.
+ */
+
+ txn_req->req.rpc->errstr =
+ XSTRDUP(MTYPE_MGMTD_ERR, "Operation on the backend timed-out");
+ txn_rpc_done(txn, txn_req);
+}
+
/*
* Send CFG_APPLY_REQs to all the backend client.
*
@@ -1516,6 +1604,7 @@ static void mgmt_txn_send_getcfg_reply_data(struct mgmt_txn_req *txn_req,
case MGMTD_TXN_PROC_SETCFG:
case MGMTD_TXN_PROC_COMMITCFG:
case MGMTD_TXN_PROC_GETTREE:
+ case MGMTD_TXN_PROC_RPC:
case MGMTD_TXN_COMMITCFG_TIMEOUT:
__log_err("Invalid Txn-Req-Event %u", txn_req->req_event);
break;
@@ -1721,6 +1810,7 @@ static struct mgmt_txn_ctx *mgmt_txn_create_new(uint64_t session_id,
mgmt_txn_reqs_init(&txn->set_cfg_reqs);
mgmt_txn_reqs_init(&txn->get_cfg_reqs);
mgmt_txn_reqs_init(&txn->get_tree_reqs);
+ mgmt_txn_reqs_init(&txn->rpc_reqs);
txn->commit_cfg_req = NULL;
txn->refcount = 0;
if (!mgmt_txn_mm->next_txn_id)
@@ -1890,6 +1980,7 @@ static void mgmt_txn_register_event(struct mgmt_txn_ctx *txn,
&txn->comm_cfg_timeout);
break;
case MGMTD_TXN_PROC_GETTREE:
+ case MGMTD_TXN_PROC_RPC:
assert(!"code bug do not register this event");
break;
}
@@ -2496,6 +2587,64 @@ reply:
return 0;
}
+int mgmt_txn_send_rpc(uint64_t txn_id, uint64_t req_id, uint64_t clients,
+ LYD_FORMAT result_type, const char *xpath,
+ const char *data, size_t data_len)
+{
+ struct mgmt_txn_ctx *txn;
+ struct mgmt_txn_req *txn_req;
+ struct mgmt_msg_rpc *msg;
+ struct txn_req_rpc *rpc;
+ uint64_t id;
+ int ret;
+
+ txn = mgmt_txn_id2ctx(txn_id);
+ if (!txn)
+ return -1;
+
+ txn_req = mgmt_txn_req_alloc(txn, req_id, MGMTD_TXN_PROC_RPC);
+ rpc = txn_req->req.rpc;
+ rpc->xpath = XSTRDUP(MTYPE_MGMTD_XPATH, xpath);
+ rpc->result_type = result_type;
+
+ msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_rpc, 0,
+ MTYPE_MSG_NATIVE_RPC);
+ msg->refer_id = txn_id;
+ msg->req_id = req_id;
+ msg->code = MGMT_MSG_CODE_RPC;
+ msg->request_type = result_type;
+
+ mgmt_msg_native_xpath_encode(msg, xpath);
+ if (data)
+ mgmt_msg_native_append(msg, data, data_len);
+
+ assert(clients);
+ FOREACH_BE_CLIENT_BITS (id, clients) {
+ ret = mgmt_be_send_native(id, msg);
+ if (ret) {
+ __log_err("Could not send rpc message to backend client %s",
+ mgmt_be_client_id2name(id));
+ continue;
+ }
+
+ __dbg("Sent rpc req to backend client %s",
+ mgmt_be_client_id2name(id));
+
+ /* record that we sent the request to the client */
+ rpc->sent_clients |= (1u << id);
+ }
+
+ mgmt_msg_native_free_msg(msg);
+
+ if (!rpc->sent_clients)
+ return txn_rpc_done(txn, txn_req);
+
+ event_add_timer(mgmt_txn_tm, txn_rpc_timeout, txn_req,
+ MGMTD_TXN_RPC_MAX_DELAY_SEC, &txn->rpc_timeout);
+
+ return 0;
+}
+
/*
* Error reply from the backend client.
*/
@@ -2506,6 +2655,7 @@ int mgmt_txn_notify_error(struct mgmt_be_client_adapter *adapter,
enum mgmt_be_client_id id = adapter->id;
struct mgmt_txn_ctx *txn = mgmt_txn_id2ctx(txn_id);
struct txn_req_get_tree *get_tree;
+ struct txn_req_rpc *rpc;
struct mgmt_txn_req *txn_req;
if (!txn) {
@@ -2518,6 +2668,10 @@ int mgmt_txn_notify_error(struct mgmt_be_client_adapter *adapter,
FOREACH_TXN_REQ_IN_LIST (&txn->get_tree_reqs, txn_req)
if (txn_req->req_id == req_id)
break;
+ if (!txn_req)
+ FOREACH_TXN_REQ_IN_LIST (&txn->rpc_reqs, txn_req)
+ if (txn_req->req_id == req_id)
+ break;
if (!txn_req) {
__log_err("Error reply from %s for txn-id %" PRIu64
" cannot find req_id %" PRIu64,
@@ -2538,6 +2692,17 @@ int mgmt_txn_notify_error(struct mgmt_be_client_adapter *adapter,
if (get_tree->recv_clients != get_tree->sent_clients)
return 0;
return txn_get_tree_data_done(txn, txn_req);
+ case MGMTD_TXN_PROC_RPC:
+ rpc = txn_req->req.rpc;
+ rpc->recv_clients |= (1u << id);
+ if (errstr) {
+ XFREE(MTYPE_MGMTD_ERR, rpc->errstr);
+ rpc->errstr = XSTRDUP(MTYPE_MGMTD_ERR, errstr);
+ }
+ /* check if done yet */
+ if (rpc->recv_clients != rpc->sent_clients)
+ return 0;
+ return txn_rpc_done(txn, txn_req);
/* non-native message events */
case MGMTD_TXN_PROC_SETCFG:
@@ -2625,6 +2790,77 @@ int mgmt_txn_notify_tree_data_reply(struct mgmt_be_client_adapter *adapter,
return txn_get_tree_data_done(txn, txn_req);
}
+int mgmt_txn_notify_rpc_reply(struct mgmt_be_client_adapter *adapter,
+ struct mgmt_msg_rpc_reply *reply_msg,
+ size_t msg_len)
+{
+ uint64_t txn_id = reply_msg->refer_id;
+ uint64_t req_id = reply_msg->req_id;
+ enum mgmt_be_client_id id = adapter->id;
+ struct mgmt_txn_ctx *txn = mgmt_txn_id2ctx(txn_id);
+ struct mgmt_txn_req *txn_req;
+ struct txn_req_rpc *rpc;
+ struct lyd_node *tree;
+ size_t data_len = msg_len - sizeof(*reply_msg);
+ LY_ERR err = LY_SUCCESS;
+
+ if (!txn) {
+ __log_err("RPC reply from %s for a missing txn-id %" PRIu64,
+ adapter->name, txn_id);
+ return -1;
+ }
+
+ /* Find the request. */
+ FOREACH_TXN_REQ_IN_LIST (&txn->rpc_reqs, txn_req)
+ if (txn_req->req_id == req_id)
+ break;
+ if (!txn_req) {
+ __log_err("RPC reply from %s for txn-id %" PRIu64
+ " missing req_id %" PRIu64,
+ adapter->name, txn_id, req_id);
+ return -1;
+ }
+
+ rpc = txn_req->req.rpc;
+
+ tree = NULL;
+ if (data_len)
+ err = yang_parse_rpc(rpc->xpath, reply_msg->result_type,
+ reply_msg->data, true, &tree);
+ if (err) {
+ __log_err("RPC reply from %s for txn-id %" PRIu64
+ " req_id %" PRIu64 " error parsing result of type %u: %s",
+ adapter->name, txn_id, req_id, reply_msg->result_type,
+ ly_strerrcode(err));
+ }
+ if (!err && tree) {
+ if (!rpc->client_results)
+ rpc->client_results = tree;
+ else
+ err = lyd_merge_siblings(&rpc->client_results, tree,
+ LYD_MERGE_DESTRUCT);
+ if (err) {
+ __log_err("RPC reply from %s for txn-id %" PRIu64
+ " req_id %" PRIu64 " error merging result: %s",
+ adapter->name, txn_id, req_id,
+ ly_strerrcode(err));
+ }
+ }
+ if (err) {
+ XFREE(MTYPE_MGMTD_ERR, rpc->errstr);
+ rpc->errstr = XSTRDUP(MTYPE_MGMTD_ERR,
+ "Cannot parse result from the backend");
+ }
+
+ rpc->recv_clients |= (1u << id);
+
+ /* check if done yet */
+ if (rpc->recv_clients != rpc->sent_clients)
+ return 0;
+
+ return txn_rpc_done(txn, txn_req);
+}
+
void mgmt_txn_status_write(struct vty *vty)
{
struct mgmt_txn_ctx *txn;
diff --git a/mgmtd/mgmt_txn.h b/mgmtd/mgmt_txn.h
index aeb74469f1..b6ca288675 100644
--- a/mgmtd/mgmt_txn.h
+++ b/mgmtd/mgmt_txn.h
@@ -21,6 +21,7 @@
#define MGMTD_TXN_CFG_COMMIT_MAX_DELAY_SEC 600
#define MGMTD_TXN_GET_TREE_MAX_DELAY_SEC 600
+#define MGMTD_TXN_RPC_MAX_DELAY_SEC 60
#define MGMTD_TXN_CLEANUP_DELAY_USEC 10
@@ -48,7 +49,8 @@ struct mgmt_edit_req;
enum mgmt_txn_type {
MGMTD_TXN_TYPE_NONE = 0,
MGMTD_TXN_TYPE_CONFIG,
- MGMTD_TXN_TYPE_SHOW
+ MGMTD_TXN_TYPE_SHOW,
+ MGMTD_TXN_TYPE_RPC,
};
static inline const char *mgmt_txn_type2str(enum mgmt_txn_type type)
@@ -60,6 +62,8 @@ static inline const char *mgmt_txn_type2str(enum mgmt_txn_type type)
return "CONFIG";
case MGMTD_TXN_TYPE_SHOW:
return "SHOW";
+ case MGMTD_TXN_TYPE_RPC:
+ return "RPC";
}
return "Unknown";
@@ -246,6 +250,25 @@ mgmt_txn_send_edit(uint64_t txn_id, uint64_t req_id, Mgmtd__DatastoreId ds_id,
LYD_FORMAT request_type, uint8_t flags, uint8_t operation,
const char *xpath, const char *data);
+/**
+ * Send RPC request.
+ *
+ * Args:
+ * txn_id: Transaction identifier.
+ * req_id: FE client request identifier.
+ * clients: Bitmask of clients to send RPC to.
+ * result_type: LYD_FORMAT result format.
+ * xpath: The xpath of the RPC.
+ * data: The input parameters data tree.
+ * data_len: The length of the input parameters data.
+ *
+ * Return:
+ * 0 on success.
+ */
+extern int mgmt_txn_send_rpc(uint64_t txn_id, uint64_t req_id, uint64_t clients,
+ LYD_FORMAT result_type, const char *xpath,
+ const char *data, size_t data_len);
+
/*
* Notifiy backend adapter on connection.
*/
@@ -312,6 +335,18 @@ extern int mgmt_txn_notify_tree_data_reply(struct mgmt_be_client_adapter *adapte
struct mgmt_msg_tree_data *data_msg,
size_t msg_len);
+/**
+ * Process a reply from a backend client to our RPC request
+ *
+ * Args:
+ * adapter: The adapter that received the result.
+ * reply_msg: The message from the backend.
+ * msg_len: Total length of the message.
+ */
+extern int mgmt_txn_notify_rpc_reply(struct mgmt_be_client_adapter *adapter,
+ struct mgmt_msg_rpc_reply *reply_msg,
+ size_t msg_len);
+
/*
* Dump transaction status to vty.
*/
diff --git a/mgmtd/mgmt_vty.c b/mgmtd/mgmt_vty.c
index 61d0760e05..8ccb463577 100644
--- a/mgmtd/mgmt_vty.c
+++ b/mgmtd/mgmt_vty.c
@@ -296,6 +296,21 @@ DEFPY(mgmt_edit, mgmt_edit_cmd,
return CMD_SUCCESS;
}
+DEFPY(mgmt_rpc, mgmt_rpc_cmd,
+ "mgmt rpc XPATH [json|xml]$fmt [DATA]",
+ MGMTD_STR
+ "Invoke RPC\n"
+ "XPath expression specifying the YANG data path\n"
+ "JSON input format (default)\n"
+ "XML input format\n"
+ "Input data tree\n")
+{
+ LYD_FORMAT format = (fmt && fmt[0] == 'x') ? LYD_XML : LYD_JSON;
+
+ vty_mgmt_send_rpc_req(vty, format, xpath, data);
+ return CMD_SUCCESS;
+}
+
DEFPY(show_mgmt_get_config, show_mgmt_get_config_cmd,
"show mgmt get-config [candidate|operational|running]$dsname WORD$path",
SHOW_STR MGMTD_STR
@@ -702,6 +717,7 @@ void mgmt_vty_init(void)
install_element(CONFIG_NODE, &mgmt_remove_config_data_cmd);
install_element(CONFIG_NODE, &mgmt_replace_config_data_cmd);
install_element(CONFIG_NODE, &mgmt_edit_cmd);
+ install_element(CONFIG_NODE, &mgmt_rpc_cmd);
install_element(CONFIG_NODE, &mgmt_load_config_cmd);
install_element(CONFIG_NODE, &mgmt_save_config_cmd);
install_element(CONFIG_NODE, &mgmt_rollback_cmd);
diff --git a/mgmtd/subdir.am b/mgmtd/subdir.am
index 5182c4a47d..14544c4f05 100644
--- a/mgmtd/subdir.am
+++ b/mgmtd/subdir.am
@@ -16,6 +16,7 @@ clippy_scan += \
lib_LTLIBRARIES += mgmtd/libmgmt_be_nb.la
mgmtd_libmgmt_be_nb_la_SOURCES = \
+ mgmtd/mgmt_be_nb.c \
zebra/zebra_cli.c \
# end
nodist_mgmtd_libmgmt_be_nb_la_SOURCES = \
diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c
index f4202a4a29..d3ff759d33 100644
--- a/ospf6d/ospf6_abr.c
+++ b/ospf6d/ospf6_abr.c
@@ -1275,8 +1275,6 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
continue;
}
- list_delete_all_node(old_route->nh_list);
- ospf6_route_copy_nexthops(old_route, route);
old_entry_updated = true;
for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode,
@@ -1330,6 +1328,15 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
}
}
+ /* We added a path or updated a path's nexthops above,
+ * recompute (old) route nexthops by merging all path nexthops
+ */
+ list_delete_all_node(old_route->nh_list);
+ for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode, o_path)) {
+ ospf6_merge_nexthops(old_route->nh_list,
+ o_path->nh_list);
+ }
+
if (is_debug)
zlog_debug(
"%s: Update route: %s %p old cost %u new cost %u nh %u",
diff --git a/ospf6d/ospf6_abr.h b/ospf6d/ospf6_abr.h
index 7bb1619133..52686ed49f 100644
--- a/ospf6d/ospf6_abr.h
+++ b/ospf6d/ospf6_abr.h
@@ -33,11 +33,12 @@ struct ospf6_inter_router_lsa {
uint32_t router_id;
};
-#define OSPF6_ABR_SUMMARY_METRIC(E) (ntohl ((E)->metric & htonl (0x00ffffff)))
+#define OSPF6_ABR_SUMMARY_METRIC(E) \
+ (ntohl((E)->metric & htonl(OSPF6_EXT_PATH_METRIC_MAX)))
#define OSPF6_ABR_SUMMARY_METRIC_SET(E, C) \
{ \
(E)->metric &= htonl(0x00000000); \
- (E)->metric |= htonl(0x00ffffff) & htonl(C); \
+ (E)->metric |= htonl(OSPF6_EXT_PATH_METRIC_MAX) & htonl(C); \
}
#define OSPF6_ABR_RANGE_CLEAR_COST(range) (range->path.cost = OSPF_AREA_RANGE_COST_UNSPEC)
diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c
index d1c2b8bfc9..701704cdc8 100644
--- a/ospf6d/ospf6_asbr.c
+++ b/ospf6d/ospf6_asbr.c
@@ -14,6 +14,7 @@
#include "table.h"
#include "plist.h"
#include "frrevent.h"
+#include "frrstr.h"
#include "linklist.h"
#include "lib/northbound_cli.h"
@@ -1426,10 +1427,9 @@ static void ospf6_external_lsa_fwd_addr_set(struct ospf6 *ospf6,
}
void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex,
- struct prefix *prefix,
- unsigned int nexthop_num,
- const struct in6_addr *nexthop,
- route_tag_t tag, struct ospf6 *ospf6)
+ struct prefix *prefix, unsigned int nexthop_num,
+ const struct in6_addr *nexthop, route_tag_t tag,
+ struct ospf6 *ospf6, uint32_t metric)
{
route_map_result_t ret;
struct ospf6_route troute;
@@ -1472,6 +1472,7 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex,
if (ROUTEMAP(red)) {
troute.route_option = &tinfo;
troute.ospf6 = ospf6;
+ troute.path.redistribute_cost = metric;
tinfo.ifindex = ifindex;
tinfo.tag = tag;
@@ -1924,7 +1925,7 @@ static void ospf6_redistribute_default_set(struct ospf6 *ospf6, int originate)
case DEFAULT_ORIGINATE_ALWAYS:
ospf6_asbr_redistribute_add(DEFAULT_ROUTE, 0,
(struct prefix *)&p, 0, &nexthop, 0,
- ospf6);
+ ospf6, 0);
break;
}
}
@@ -2153,25 +2154,81 @@ static const struct route_map_rule_cmd
ospf6_routemap_rule_set_metric_type_free,
};
+struct ospf6_metric {
+ enum { metric_increment, metric_decrement, metric_absolute } type;
+ bool used;
+ uint32_t metric;
+};
+
static enum route_map_cmd_result_t
ospf6_routemap_rule_set_metric(void *rule, const struct prefix *prefix,
void *object)
{
- char *metric = rule;
- struct ospf6_route *route = object;
+ struct ospf6_metric *metric;
+ struct ospf6_route *route;
+
+ /* Fetch routemap's rule information. */
+ metric = rule;
+ route = object;
+
+ /* Set metric out value. */
+ if (!metric->used)
+ return RMAP_OKAY;
+
+ if (route->path.redistribute_cost > OSPF6_EXT_PATH_METRIC_MAX)
+ route->path.redistribute_cost = OSPF6_EXT_PATH_METRIC_MAX;
+
+ if (metric->type == metric_increment) {
+ route->path.cost = route->path.redistribute_cost +
+ metric->metric;
+
+ /* Check overflow */
+ if (route->path.cost > OSPF6_EXT_PATH_METRIC_MAX ||
+ route->path.cost < metric->metric)
+ route->path.cost = OSPF6_EXT_PATH_METRIC_MAX;
+ } else if (metric->type == metric_decrement) {
+ route->path.cost = route->path.redistribute_cost -
+ metric->metric;
+
+ /* Check overflow */
+ if (route->path.cost == 0 ||
+ route->path.cost > route->path.redistribute_cost)
+ route->path.cost = 1;
+ } else if (metric->type == metric_absolute)
+ route->path.cost = metric->metric;
- route->path.cost = atoi(metric);
return RMAP_OKAY;
}
static void *ospf6_routemap_rule_set_metric_compile(const char *arg)
{
- uint32_t metric;
- char *endp;
- metric = strtoul(arg, &endp, 0);
- if (metric > OSPF_LS_INFINITY || *endp != '\0')
- return NULL;
- return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+ struct ospf6_metric *metric;
+
+ metric = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(*metric));
+ metric->used = false;
+
+ if (all_digit(arg))
+ metric->type = metric_absolute;
+
+ if ((arg[0] == '+') && all_digit(arg + 1)) {
+ metric->type = metric_increment;
+ arg++;
+ }
+
+ if ((arg[0] == '-') && all_digit(arg + 1)) {
+ metric->type = metric_decrement;
+ arg++;
+ }
+
+ metric->metric = strtoul(arg, NULL, 10);
+
+ if (metric->metric > OSPF6_EXT_PATH_METRIC_MAX)
+ metric->metric = OSPF6_EXT_PATH_METRIC_MAX;
+
+ if (metric->metric)
+ metric->used = true;
+
+ return metric;
}
static void ospf6_routemap_rule_set_metric_free(void *rule)
diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h
index d63e467278..21e6d898e8 100644
--- a/ospf6d/ospf6_asbr.h
+++ b/ospf6d/ospf6_asbr.h
@@ -94,11 +94,13 @@ struct ospf6_as_external_lsa {
#define OSPF6_ASBR_BIT_F ntohl (0x02000000)
#define OSPF6_ASBR_BIT_E ntohl (0x04000000)
-#define OSPF6_ASBR_METRIC(E) (ntohl ((E)->bits_metric & htonl (0x00ffffff)))
+#define OSPF6_ASBR_METRIC(E) \
+ (ntohl((E)->bits_metric & htonl(OSPF6_EXT_PATH_METRIC_MAX)))
#define OSPF6_ASBR_METRIC_SET(E, C) \
{ \
- (E)->bits_metric &= htonl(0xff000000); \
- (E)->bits_metric |= htonl(0x00ffffff) & htonl(C); \
+ (E)->bits_metric &= htonl(~OSPF6_EXT_PATH_METRIC_MAX); \
+ (E)->bits_metric |= htonl(OSPF6_EXT_PATH_METRIC_MAX) & \
+ htonl(C); \
}
extern void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa);
@@ -115,7 +117,8 @@ extern void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex,
struct prefix *prefix,
unsigned int nexthop_num,
const struct in6_addr *nexthop,
- route_tag_t tag, struct ospf6 *ospf6);
+ route_tag_t tag, struct ospf6 *ospf6,
+ uint32_t metric);
extern void ospf6_asbr_redistribute_remove(int type, ifindex_t ifindex,
struct prefix *prefix,
struct ospf6 *ospf6);
diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h
index 2c1d17efc3..d3c1804658 100644
--- a/ospf6d/ospf6_route.h
+++ b/ospf6d/ospf6_route.h
@@ -115,6 +115,7 @@ struct ospf6_path {
/* Cost */
uint8_t metric_type;
uint32_t cost;
+ uint32_t redistribute_cost;
struct prefix ls_prefix;
@@ -139,6 +140,8 @@ struct ospf6_path {
#define OSPF6_PATH_COST_IS_CONFIGURED(path) (path.u.cost_config != OSPF_AREA_RANGE_COST_UNSPEC)
+#define OSPF6_EXT_PATH_METRIC_MAX 0x00ffffff
+
#include "prefix.h"
#include "table.h"
#include "bitfield.h"
diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c
index 3245578b07..911f3567d4 100644
--- a/ospf6d/ospf6_zebra.c
+++ b/ospf6d/ospf6_zebra.c
@@ -289,7 +289,7 @@ static int ospf6_zebra_read_route(ZAPI_CALLBACK_ARGS)
if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
ospf6_asbr_redistribute_add(api.type, ifindex, &api.prefix,
api.nexthop_num, nexthop, api.tag,
- ospf6);
+ ospf6, api.metric);
else
ospf6_asbr_redistribute_remove(api.type, ifindex, &api.prefix,
ospf6);
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index 3f8731f369..301320bb0a 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -8208,6 +8208,8 @@ static int ospf_vty_dead_interval_set(struct vty *vty, const char *interval_str,
ospf_nbr_timer_update(oi);
}
+ if (params->fast_hello != OSPF_FAST_HELLO_DEFAULT)
+ ospf_reset_hello_timer(ifp, addr, false);
return CMD_SUCCESS;
}
diff --git a/python/clippy/__init__.py b/python/clippy/__init__.py
index 60119fbac0..668724ab2a 100644
--- a/python/clippy/__init__.py
+++ b/python/clippy/__init__.py
@@ -20,6 +20,7 @@ from _clippy import (
CMD_ATTR_HIDDEN,
CMD_ATTR_DEPRECATED,
CMD_ATTR_NOSH,
+ elf_notes,
)
diff --git a/python/clippy/elf.py b/python/clippy/elf.py
index cc442eeda9..fd348428f0 100644
--- a/python/clippy/elf.py
+++ b/python/clippy/elf.py
@@ -458,7 +458,16 @@ class ELFSubset(object):
- `this[123:str]` - extract until null byte. The slice stop value is
the `str` type (or, technically, `unicode`.)
"""
- return self._obj[k]
+ if k.start < getattr(self._obj, "len", float("+Inf")):
+ return self._obj[k]
+
+ real_sect = self._elffile.get_section_addr(self._obj.sh_addr + k.start)
+ offs = self._obj.sh_addr - real_sect.sh_addr
+ if k.stop is str:
+ new_k = slice(k.start + offs, str)
+ else:
+ new_k = slice(k.start + offs, k.stop + offs)
+ return real_sect[new_k]
def getreloc(self, offset):
"""
diff --git a/python/xrelfo.py b/python/xrelfo.py
index a40b19e5fb..07cd74071f 100644
--- a/python/xrelfo.py
+++ b/python/xrelfo.py
@@ -22,7 +22,7 @@ import argparse
from clippy.uidhash import uidhash
from clippy.elf import *
-from clippy import frr_top_src, CmdAttr
+from clippy import frr_top_src, CmdAttr, elf_notes
from tiabwarfo import FieldApplicator
from xref2vtysh import CommandEntry
@@ -327,6 +327,7 @@ class Xrelfo(dict):
}
)
self._xrefs = []
+ self.note_warn = False
def load_file(self, filename):
orig_filename = filename
@@ -395,6 +396,15 @@ class Xrelfo(dict):
ptrs = edf.iter_data(XrefPtr, slice(start, end))
else:
+ if elf_notes:
+ self.note_warn = True
+ sys.stderr.write(
+ """%s: warning: binary has no FRRouting.XREF note
+%s- one of FRR_MODULE_SETUP, FRR_DAEMON_INFO or XREF_SETUP must be used
+"""
+ % (orig_filename, orig_filename)
+ )
+
xrefarray = edf.get_section("xref_array")
if xrefarray is None:
raise ValueError("file has neither xref note nor xref_array section")
@@ -471,6 +481,9 @@ def _main(args):
sys.stderr.write("while processing %s:\n" % (fn))
traceback.print_exc()
+ if xrelfo.note_warn and args.Werror:
+ errors += 1
+
for option in dir(args):
if option.startswith("W") and option != "Werror":
checks = sorted(xrelfo.check(args))
diff --git a/qpb/qpb.c b/qpb/qpb.c
index 63454f1156..6258178578 100644
--- a/qpb/qpb.c
+++ b/qpb/qpb.c
@@ -10,3 +10,8 @@
/*
* Main file for the qpb library.
*/
+
+#include "config.h"
+#include "xref.h"
+
+XREF_SETUP();
diff --git a/ripd/rip_cli.c b/ripd/rip_cli.c
index 4d4349b81a..7066485be0 100644
--- a/ripd/rip_cli.c
+++ b/ripd/rip_cli.c
@@ -1233,6 +1233,23 @@ DEFPY_YANG(no_rip_distribute_list_prefix,
return nb_cli_apply_changes(vty, NULL);
}
+/*
+ * XPath: /frr-ripd:clear-rip-route
+ */
+DEFPY_YANG (clear_ip_rip,
+ clear_ip_rip_cmd,
+ "clear ip rip [vrf WORD]",
+ CLEAR_STR
+ IP_STR
+ "Clear IP RIP database\n"
+ VRF_CMD_HELP_STR)
+{
+ if (vrf)
+ nb_cli_rpc_enqueue(vty, "vrf", vrf);
+
+ return nb_cli_rpc(vty, "/frr-ripd:clear-rip-route", NULL);
+}
+
/* RIP node structure. */
static struct cmd_node rip_node = {
.name = "rip",
@@ -1295,6 +1312,8 @@ void rip_cli_init(void)
install_element(INTERFACE_NODE, &ip_rip_bfd_profile_cmd);
install_element(INTERFACE_NODE, &no_ip_rip_bfd_profile_cmd);
+ install_element(ENABLE_NODE, &clear_ip_rip_cmd);
+
if_rmap_init(RIP_NODE);
}
/* clang-format off */
diff --git a/ripd/rip_nb_rpcs.c b/ripd/rip_nb_rpcs.c
index bbe3d0f0f8..5d3d7145b1 100644
--- a/ripd/rip_nb_rpcs.c
+++ b/ripd/rip_nb_rpcs.c
@@ -68,12 +68,11 @@ static void clear_rip_route(struct rip *rip)
int clear_rip_route_rpc(struct nb_cb_rpc_args *args)
{
struct rip *rip;
- struct yang_data *yang_vrf;
- yang_vrf = yang_data_list_find(args->input, "%s/%s", args->xpath,
- "input/vrf");
- if (yang_vrf) {
- rip = rip_lookup_by_vrf_name(yang_vrf->value);
+ if (args->input && yang_dnode_exists(args->input, "vrf")) {
+ const char *name = yang_dnode_get_string(args->input, "vrf");
+
+ rip = rip_lookup_by_vrf_name(name);
if (rip)
clear_rip_route(rip);
} else {
diff --git a/ripd/ripd.c b/ripd/ripd.c
index e3220a9267..b8a140c9ca 100644
--- a/ripd/ripd.c
+++ b/ripd/ripd.c
@@ -3254,38 +3254,6 @@ DEFUN (show_ip_rip_status,
return CMD_SUCCESS;
}
-#include "ripd/ripd_clippy.c"
-
-/*
- * XPath: /frr-ripd:clear-rip-route
- */
-DEFPY_YANG (clear_ip_rip,
- clear_ip_rip_cmd,
- "clear ip rip [vrf WORD]",
- CLEAR_STR
- IP_STR
- "Clear IP RIP database\n"
- VRF_CMD_HELP_STR)
-{
- struct list *input;
- int ret;
-
- input = list_new();
- if (vrf) {
- struct yang_data *yang_vrf;
-
- yang_vrf = yang_data_new("/frr-ripd:clear-rip-route/input/vrf",
- vrf);
- listnode_add(input, yang_vrf);
- }
-
- ret = nb_cli_rpc(vty, "/frr-ripd:clear-rip-route", input, NULL);
-
- list_delete(&input);
-
- return ret;
-}
-
/* Distribute-list update functions. */
static void rip_distribute_update(struct distribute_ctx *ctx,
struct distribute *dist)
@@ -3660,7 +3628,6 @@ void rip_init(void)
/* Install rip commands. */
install_element(VIEW_NODE, &show_ip_rip_cmd);
install_element(VIEW_NODE, &show_ip_rip_status_cmd);
- install_element(ENABLE_NODE, &clear_ip_rip_cmd);
/* Debug related init. */
rip_debug_init();
diff --git a/ripd/subdir.am b/ripd/subdir.am
index 7fb3726077..aed8d249fe 100644
--- a/ripd/subdir.am
+++ b/ripd/subdir.am
@@ -33,7 +33,6 @@ ripd_ripd_SOURCES = \
clippy_scan += \
ripd/rip_bfd.c \
ripd/rip_cli.c \
- ripd/ripd.c \
# end
noinst_HEADERS += \
diff --git a/ripngd/ripng_cli.c b/ripngd/ripng_cli.c
index 4806861fe0..99cb68ea32 100644
--- a/ripngd/ripng_cli.c
+++ b/ripngd/ripng_cli.c
@@ -624,6 +624,23 @@ DEFPY_YANG(no_ripng_ipv6_distribute_list_prefix,
return nb_cli_apply_changes(vty, NULL);
}
+/*
+ * XPath: /frr-ripngd:clear-ripng-route
+ */
+DEFPY_YANG (clear_ipv6_rip,
+ clear_ipv6_rip_cmd,
+ "clear ipv6 ripng [vrf WORD]",
+ CLEAR_STR
+ IPV6_STR
+ "Clear IPv6 RIP database\n"
+ VRF_CMD_HELP_STR)
+{
+ if (vrf)
+ nb_cli_rpc_enqueue(vty, "vrf", vrf);
+
+ return nb_cli_rpc(vty, "/frr-ripngd:clear-ripng-route", NULL);
+}
+
/* RIPng node structure. */
static struct cmd_node cmd_ripng_node = {
.name = "ripng",
@@ -663,6 +680,8 @@ void ripng_cli_init(void)
install_element(INTERFACE_NODE, &ipv6_ripng_split_horizon_cmd);
+ install_element(ENABLE_NODE, &clear_ipv6_rip_cmd);
+
if_rmap_init(RIPNG_NODE);
}
diff --git a/ripngd/ripng_nb_rpcs.c b/ripngd/ripng_nb_rpcs.c
index b23572d492..5498bbfc8e 100644
--- a/ripngd/ripng_nb_rpcs.c
+++ b/ripngd/ripng_nb_rpcs.c
@@ -70,12 +70,11 @@ static void clear_ripng_route(struct ripng *ripng)
int clear_ripng_route_rpc(struct nb_cb_rpc_args *args)
{
struct ripng *ripng;
- struct yang_data *yang_vrf;
- yang_vrf = yang_data_list_find(args->input, "%s/%s", args->xpath,
- "input/vrf");
- if (yang_vrf) {
- ripng = ripng_lookup_by_vrf_name(yang_vrf->value);
+ if (args->input && yang_dnode_exists(args->input, "vrf")) {
+ const char *name = yang_dnode_get_string(args->input, "vrf");
+
+ ripng = ripng_lookup_by_vrf_name(name);
if (ripng)
clear_ripng_route(ripng);
} else {
diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c
index 80b9013e0f..f4dadf377d 100644
--- a/ripngd/ripngd.c
+++ b/ripngd/ripngd.c
@@ -2231,38 +2231,6 @@ DEFUN (show_ipv6_ripng_status,
return CMD_SUCCESS;
}
-#include "ripngd/ripngd_clippy.c"
-
-/*
- * XPath: /frr-ripngd:clear-ripng-route
- */
-DEFPY_YANG (clear_ipv6_rip,
- clear_ipv6_rip_cmd,
- "clear ipv6 ripng [vrf WORD]",
- CLEAR_STR
- IPV6_STR
- "Clear IPv6 RIP database\n"
- VRF_CMD_HELP_STR)
-{
- struct list *input;
- int ret;
-
- input = list_new();
- if (vrf) {
- struct yang_data *yang_vrf;
-
- yang_vrf = yang_data_new(
- "/frr-ripngd:clear-ripng-route/input/vrf", vrf);
- listnode_add(input, yang_vrf);
- }
-
- ret = nb_cli_rpc(vty, "/frr-ripngd:clear-ripng-route", input, NULL);
-
- list_delete(&input);
-
- return ret;
-}
-
/* Update ECMP routes to zebra when ECMP is disabled. */
void ripng_ecmp_disable(struct ripng *ripng)
{
@@ -2680,7 +2648,6 @@ void ripng_init(void)
/* Install ripng commands. */
install_element(VIEW_NODE, &show_ipv6_ripng_cmd);
install_element(VIEW_NODE, &show_ipv6_ripng_status_cmd);
- install_element(ENABLE_NODE, &clear_ipv6_rip_cmd);
ripng_if_init();
ripng_debug_init();
diff --git a/ripngd/subdir.am b/ripngd/subdir.am
index a88114432d..83e376b555 100644
--- a/ripngd/subdir.am
+++ b/ripngd/subdir.am
@@ -27,7 +27,6 @@ ripngd_ripngd_SOURCES = \
clippy_scan += \
ripngd/ripng_cli.c \
- ripngd/ripngd.c \
# end
noinst_HEADERS += \
diff --git a/tests/lib/northbound/test_oper_data.c b/tests/lib/northbound/test_oper_data.c
index 8f7e7c5f8c..321f158668 100644
--- a/tests/lib/northbound/test_oper_data.c
+++ b/tests/lib/northbound/test_oper_data.c
@@ -14,6 +14,7 @@
#include "lib_vty.h"
#include "log.h"
#include "northbound.h"
+#include "northbound_cli.h"
static struct event_loop *master;
@@ -200,6 +201,19 @@ static struct yang_data *frr_test_module_vrfs_vrf_routes_route_active_get_elem(
return NULL;
}
+/*
+ * XPath: /frr-test-module:frr-test-module/vrfs/vrf/ping
+ */
+static int frr_test_module_vrfs_vrf_ping(struct nb_cb_rpc_args *args)
+{
+ const char *vrf = yang_dnode_get_string(args->input, "../name");
+ const char *data = yang_dnode_get_string(args->input, "data");
+
+ yang_dnode_rpc_output_add(args->output, "vrf", vrf);
+ yang_dnode_rpc_output_add(args->output, "data-out", data);
+
+ return NB_OK;
+}
/*
* XPath: /frr-test-module:frr-test-module/c1value
@@ -263,6 +277,10 @@ const struct frr_yang_module_info frr_test_module_info = {
.cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_active_get_elem,
},
{
+ .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/ping",
+ .cbs.rpc = frr_test_module_vrfs_vrf_ping,
+ },
+ {
.xpath = "/frr-test-module:frr-test-module/c1value",
.cbs.get_elem = frr_test_module_c1value_get_elem,
},
@@ -277,6 +295,33 @@ const struct frr_yang_module_info frr_test_module_info = {
};
/* clang-format on */
+DEFUN(test_rpc, test_rpc_cmd, "test rpc",
+ "Test\n"
+ "RPC\n")
+{
+ struct lyd_node *output = NULL;
+ char xpath[XPATH_MAXLEN];
+ int ret;
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-test-module:frr-test-module/vrfs/vrf[name='testname']/ping");
+
+ nb_cli_rpc_enqueue(vty, "data", "testdata");
+
+ ret = nb_cli_rpc(vty, xpath, &output);
+ if (ret != CMD_SUCCESS) {
+ vty_out(vty, "RPC failed\n");
+ return ret;
+ }
+
+ vty_out(vty, "vrf %s data %s\n", yang_dnode_get_string(output, "vrf"),
+ yang_dnode_get_string(output, "data-out"));
+
+ yang_dnode_free(output);
+
+ return CMD_SUCCESS;
+}
+
static const struct frr_yang_module_info *const modules[] = {
&frr_test_module_info,
};
@@ -416,6 +461,8 @@ int main(int argc, char **argv)
lib_cmd_init();
nb_init(master, modules, array_size(modules), false);
+ install_element(ENABLE_NODE, &test_rpc_cmd);
+
/* Create artificial data. */
create_data(num_vrfs, num_interfaces, num_routes);
diff --git a/tests/lib/northbound/test_oper_data.in b/tests/lib/northbound/test_oper_data.in
index a6c4f874f5..f7c44cad31 100644
--- a/tests/lib/northbound/test_oper_data.in
+++ b/tests/lib/northbound/test_oper_data.in
@@ -1 +1,2 @@
show yang operational-data /frr-test-module:frr-test-module
+test rpc
diff --git a/tests/lib/northbound/test_oper_data.refout b/tests/lib/northbound/test_oper_data.refout
index aa930fe127..7c56564143 100644
--- a/tests/lib/northbound/test_oper_data.refout
+++ b/tests/lib/northbound/test_oper_data.refout
@@ -119,5 +119,7 @@ test# show yang operational-data /frr-test-module:frr-test-module
}
}
}
+test# test rpc
+vrf testname data testdata
test#
end.
diff --git a/tests/topotests/bgp_path_attribute_discard/peer1/exabgp.cfg b/tests/topotests/bgp_path_attribute_discard/peer1/exabgp.cfg
index 7fb9210ecf..dccec7d154 100644
--- a/tests/topotests/bgp_path_attribute_discard/peer1/exabgp.cfg
+++ b/tests/topotests/bgp_path_attribute_discard/peer1/exabgp.cfg
@@ -1,8 +1,8 @@
neighbor 10.0.0.1 {
- router-id 10.0.0.2;
- local-address 10.0.0.2;
- local-as 65001;
- peer-as 65002;
+ router-id 10.0.0.254;
+ local-address 10.0.0.254;
+ local-as 65254;
+ peer-as 65001;
capability {
route-refresh;
@@ -12,13 +12,28 @@ neighbor 10.0.0.1 {
route 192.168.100.101/32 {
atomic-aggregate;
community 65001:101;
- next-hop 10.0.0.2;
+ next-hop 10.0.0.254;
}
route 192.168.100.102/32 {
- originator-id 10.0.0.2;
+ originator-id 10.0.0.254;
community 65001:102;
- next-hop 10.0.0.2;
+ next-hop 10.0.0.254;
+ }
+ }
+}
+
+neighbor 10.0.0.2 {
+ router-id 10.0.0.254;
+ local-address 10.0.0.254;
+ local-as 65254;
+ peer-as 65254;
+
+ static {
+ route 192.168.100.101/32 {
+ # AIGP invalid attribute: flagged as transitive + optional.
+ attribute [0x1a 0xc0 0x00000064];
+ next-hop 10.0.0.254;
}
}
}
diff --git a/tests/topotests/bgp_path_attribute_discard/r1/bgpd.conf b/tests/topotests/bgp_path_attribute_discard/r1/bgpd.conf
deleted file mode 100644
index c96f354cc5..0000000000
--- a/tests/topotests/bgp_path_attribute_discard/r1/bgpd.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-!
-router bgp 65002
- no bgp ebgp-requires-policy
- neighbor 10.0.0.2 remote-as external
- neighbor 10.0.0.2 timers 3 10
-!
diff --git a/tests/topotests/bgp_path_attribute_discard/r1/frr.conf b/tests/topotests/bgp_path_attribute_discard/r1/frr.conf
new file mode 100644
index 0000000000..ae7fbdd9a9
--- /dev/null
+++ b/tests/topotests/bgp_path_attribute_discard/r1/frr.conf
@@ -0,0 +1,9 @@
+!
+interface r1-eth0
+ ip address 10.0.0.1/24
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 10.0.0.254 remote-as external
+ neighbor 10.0.0.254 timers 3 10
+!
diff --git a/tests/topotests/bgp_path_attribute_discard/r1/zebra.conf b/tests/topotests/bgp_path_attribute_discard/r1/zebra.conf
deleted file mode 100644
index 51a1b2657c..0000000000
--- a/tests/topotests/bgp_path_attribute_discard/r1/zebra.conf
+++ /dev/null
@@ -1,4 +0,0 @@
-!
-interface r1-eth0
- ip address 10.0.0.1/24
-!
diff --git a/tests/topotests/bgp_path_attribute_discard/r2/frr.conf b/tests/topotests/bgp_path_attribute_discard/r2/frr.conf
new file mode 100644
index 0000000000..1dafbdd8e1
--- /dev/null
+++ b/tests/topotests/bgp_path_attribute_discard/r2/frr.conf
@@ -0,0 +1,10 @@
+!
+interface r2-eth0
+ ip address 10.0.0.2/24
+!
+router bgp 65254
+ no bgp ebgp-requires-policy
+ neighbor 10.0.0.254 remote-as internal
+ neighbor 10.0.0.254 timers 3 10
+ neighbor 10.0.0.254 path-attribute discard 26
+!
diff --git a/tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py b/tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py
index c97cd0bdda..bd8cd8e18a 100644
--- a/tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py
+++ b/tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py
@@ -31,10 +31,12 @@ pytestmark = [pytest.mark.bgpd]
def build_topo(tgen):
r1 = tgen.add_router("r1")
- peer1 = tgen.add_exabgp_peer("peer1", ip="10.0.0.2", defaultRoute="via 10.0.0.1")
+ r2 = tgen.add_router("r2")
+ peer1 = tgen.add_exabgp_peer("peer1", ip="10.0.0.254", defaultRoute="via 10.0.0.1")
switch = tgen.add_switch("s1")
switch.add_link(r1)
+ switch.add_link(r2)
switch.add_link(peer1)
@@ -42,10 +44,10 @@ def setup_module(mod):
tgen = Topogen(build_topo, mod.__name__)
tgen.start_topology()
- router = tgen.gears["r1"]
- router.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf"))
- router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r1/bgpd.conf"))
- router.start()
+ for _, (rname, router) in enumerate(tgen.routers().items(), 1):
+ router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ tgen.start_router()
peer = tgen.gears["peer1"]
peer.start(os.path.join(CWD, "peer1"), os.path.join(CWD, "exabgp.env"))
@@ -63,6 +65,7 @@ def test_bgp_path_attribute_discard():
pytest.skip(tgen.errors)
r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
def _bgp_converge():
output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast json detail"))
@@ -103,7 +106,7 @@ def test_bgp_path_attribute_discard():
"""
configure terminal
router bgp
- neighbor 10.0.0.2 path-attribute discard 6 8
+ neighbor 10.0.0.254 path-attribute discard 6 8
"""
)
@@ -139,6 +142,28 @@ def test_bgp_path_attribute_discard():
result is None
), "Failed to discard path attributes (atomic-aggregate, community)"
+ def _bgp_check_if_aigp_invalid_attribute_discarded():
+ output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast json detail"))
+ expected = {
+ "routes": {
+ "192.168.100.101/32": {
+ "paths": [
+ {
+ "valid": True,
+ "aigpMetric": None,
+ }
+ ],
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_if_aigp_invalid_attribute_discarded)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert (
+ result is None
+ ), "Failed to discard AIGP invalid path attribute (iBGP session)"
+
def test_memory_leak():
"Run the memory leak test and report results."
diff --git a/tests/topotests/mgmt_rpc/r1/frr.conf b/tests/topotests/mgmt_rpc/r1/frr.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/mgmt_rpc/r1/frr.conf
diff --git a/tests/topotests/mgmt_rpc/test_rpc.py b/tests/topotests/mgmt_rpc/test_rpc.py
new file mode 100644
index 0000000000..618d9022ce
--- /dev/null
+++ b/tests/topotests/mgmt_rpc/test_rpc.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: ISC
+#
+# March 21 2024, Igor Ryzhov <iryzhov@nfware.com>
+#
+# Copyright (c) 2024, NFWare Inc.
+#
+
+"""
+Test YANG Notifications
+"""
+import json
+import os
+import threading
+
+import pytest
+from lib.topogen import Topogen
+from lib.topotest import json_cmp
+
+pytestmark = [pytest.mark.ripd, pytest.mark.mgmtd]
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+
+@pytest.fixture(scope="module")
+def tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+
+ topodef = {"s1": ("r1",)}
+
+ tgen = Topogen(topodef, request.module.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.load_frr_config("frr.conf")
+
+ tgen.start_router()
+ yield tgen
+ tgen.stop_topology()
+
+
+def test_backend_rpc(tgen):
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+
+ be_client_path = "/usr/lib/frr/mgmtd_testc"
+ rc, _, _ = r1.net.cmd_status(be_client_path + " --help")
+
+ if rc:
+ pytest.skip("No mgmtd_testc")
+
+ out = []
+
+ def run_testc():
+ output = r1.net.cmd_raises(
+ be_client_path + " --timeout 10 --log file:mgmt_testc.log"
+ )
+ out.append(json.loads(output))
+
+ t = threading.Thread(target=run_testc)
+ t.start()
+
+ r1.vtysh_cmd("clear ip rip vrf testname")
+
+ t.join()
+
+ jsout = out[0]
+
+ expected = {"frr-ripd:clear-rip-route": {"vrf": "testname"}}
+ result = json_cmp(jsout, expected)
+ assert result is None
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r5/ospf6d.conf b/tests/topotests/ospf6_ecmp_inter_area/r5/ospf6d.conf
index 2a6c9abd2e..24cd354118 100644
--- a/tests/topotests/ospf6_ecmp_inter_area/r5/ospf6d.conf
+++ b/tests/topotests/ospf6_ecmp_inter_area/r5/ospf6d.conf
@@ -4,31 +4,11 @@ interface r5-eth0
ipv6 ospf6 dead-interval 10
!
interface r5-eth1
- ipv6 ospf6 area 0
- ipv6 ospf6 hello-interval 2
- ipv6 ospf6 dead-interval 10
-!
-interface r5-eth2
- ipv6 ospf6 area 0
- ipv6 ospf6 hello-interval 2
- ipv6 ospf6 dead-interval 10
-!
-interface r5-eth3
ipv6 ospf6 area 1
ipv6 ospf6 hello-interval 2
ipv6 ospf6 dead-interval 10
!
-interface r5-eth4
- ipv6 ospf6 area 1
- ipv6 ospf6 hello-interval 2
- ipv6 ospf6 dead-interval 10
-!
-interface r5-eth5
- ipv6 ospf6 area 0
- ipv6 ospf6 hello-interval 2
- ipv6 ospf6 dead-interval 10
-!
-interface r5-eth6
+interface r5-eth2
ipv6 ospf6 area 0
ipv6 ospf6 hello-interval 2
ipv6 ospf6 dead-interval 10
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r6/ospf6d.conf b/tests/topotests/ospf6_ecmp_inter_area/r6/ospf6d.conf
index a1f48b51a5..4efaa318a9 100644
--- a/tests/topotests/ospf6_ecmp_inter_area/r6/ospf6d.conf
+++ b/tests/topotests/ospf6_ecmp_inter_area/r6/ospf6d.conf
@@ -1,8 +1,23 @@
interface r6-eth0
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r6-eth1
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r6-eth2
ipv6 ospf6 area 1
ipv6 ospf6 hello-interval 2
ipv6 ospf6 dead-interval 10
!
+interface r6-eth3
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
router ospf6
ospf6 router-id 10.254.254.6
redistribute connected
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r7/ospf6d.conf b/tests/topotests/ospf6_ecmp_inter_area/r7/ospf6d.conf
index 0e49b0df6c..9b7756e838 100644
--- a/tests/topotests/ospf6_ecmp_inter_area/r7/ospf6d.conf
+++ b/tests/topotests/ospf6_ecmp_inter_area/r7/ospf6d.conf
@@ -1,11 +1,22 @@
-interface lo
+interface r7-eth0
ipv6 ospf6 area 1
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
!
-interface r7-eth0
+interface r7-eth1
ipv6 ospf6 area 1
ipv6 ospf6 hello-interval 2
ipv6 ospf6 dead-interval 10
!
+interface r7-eth2
+ ipv6 ospf6 area 1
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r7-eth3
+ shutdown
+!
router ospf6
ospf6 router-id 10.254.254.7
+ redistribute connected
!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r7/zebra.conf b/tests/topotests/ospf6_ecmp_inter_area/r7/zebra.conf
index a410be8f73..1608cad0b0 100644
--- a/tests/topotests/ospf6_ecmp_inter_area/r7/zebra.conf
+++ b/tests/topotests/ospf6_ecmp_inter_area/r7/zebra.conf
@@ -3,3 +3,6 @@ ipv6 forwarding
interface lo
ipv6 address 2001:db8:7::1/64
!
+interface r7-eth2
+ ipv6 address 2001:db8:8007::1/64
+!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r8/ospf6d.conf b/tests/topotests/ospf6_ecmp_inter_area/r8/ospf6d.conf
index fb5483ce99..33c64979ca 100644
--- a/tests/topotests/ospf6_ecmp_inter_area/r8/ospf6d.conf
+++ b/tests/topotests/ospf6_ecmp_inter_area/r8/ospf6d.conf
@@ -3,6 +3,19 @@ interface r8-eth0
ipv6 ospf6 hello-interval 2
ipv6 ospf6 dead-interval 10
!
+interface r8-eth1
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r8-eth2
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r8-eth3
+ shutdown
+!
router ospf6
ospf6 router-id 10.254.254.8
redistribute connected
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r8/zebra.conf b/tests/topotests/ospf6_ecmp_inter_area/r8/zebra.conf
index 8e343d8f45..e756cd4b95 100644
--- a/tests/topotests/ospf6_ecmp_inter_area/r8/zebra.conf
+++ b/tests/topotests/ospf6_ecmp_inter_area/r8/zebra.conf
@@ -3,3 +3,6 @@ ipv6 forwarding
interface lo
ipv6 address 2001:db8:8::1/64
!
+interface r8-eth2
+ ipv6 address 2001:db8:8008::1/64
+!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r9/ospf6d.conf b/tests/topotests/ospf6_ecmp_inter_area/r9/ospf6d.conf
deleted file mode 100644
index 57fa8e32ee..0000000000
--- a/tests/topotests/ospf6_ecmp_inter_area/r9/ospf6d.conf
+++ /dev/null
@@ -1,11 +0,0 @@
-interface lo
- ipv6 ospf6 area 0
-!
-interface r9-eth0
- ipv6 ospf6 area 0
- ipv6 ospf6 hello-interval 2
- ipv6 ospf6 dead-interval 10
-!
-router ospf6
- ospf6 router-id 10.254.254.9
-!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r9/zebra.conf b/tests/topotests/ospf6_ecmp_inter_area/r9/zebra.conf
deleted file mode 100644
index e267496a98..0000000000
--- a/tests/topotests/ospf6_ecmp_inter_area/r9/zebra.conf
+++ /dev/null
@@ -1,5 +0,0 @@
-ipv6 forwarding
-!
-interface lo
- ipv6 address 2001:db8:9::1/64
-!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/test_ospf6_ecmp_inter_area.py b/tests/topotests/ospf6_ecmp_inter_area/test_ospf6_ecmp_inter_area.py
index 2eaccb8348..adf289e2de 100644
--- a/tests/topotests/ospf6_ecmp_inter_area/test_ospf6_ecmp_inter_area.py
+++ b/tests/topotests/ospf6_ecmp_inter_area/test_ospf6_ecmp_inter_area.py
@@ -3,7 +3,7 @@
# test_ospf6_ecmp_inter_area.py
#
-# Copyright (c) 2021, 2022 by Martin Buck
+# Copyright (c) 2021, 2022, 2024 by Martin Buck
# Copyright (c) 2016 by
# Network Device Education Foundation, Inc. ("NetDEF")
#
@@ -11,35 +11,42 @@
r"""
test_ospf6_ecmp_inter_area.py: Test OSPFv3 ECMP inter-area nexthop update
-Check proper removal of ECMP nexthops after a path used by one nexthop
-disappears. We remove a path by bringing down a link required by that
-path which is not adjacent to the router being checked. This is important
-because when bringing down adjacent links, the kernel might remove the
-nexthops itself without ospf6d having to do anything.
+Check proper addition and removal of ECMP nexthops in 2 cases: Parallel
+paths to one ABR and parallel ABRs. We test nexthop removal triggered by
+path removal by bringing down a link required by that path which is not
+adjacent to the router being checked. This is important because when
+bringing down adjacent links, the kernel might remove the nexthops itself
+without ospf6d having to do anything.
-Useful as a regression test for #9720.
+Useful as a regression test for #9720 and #15777.
Topology:
- .
- Area 0 . Area 1
- .
- -- R2 -- . ---- R6
- / \ ./
-R1 -- R3 -- R5 ---- R7 Area 1
- \ / \\ ..............
- -- R4 -- \--- R8 Area 0
- \
- -- R9
-
-We check routes on R1, primarily those towards R6/7/8/9. Those to R6/7 are
-inter-area routes with R5 being ABR, those to R8/9 are intra-area routes
-and are used for reference. R6/R8 announce external routes, R7/R9 announce
-internal routes.
+ .
+ Area 0 . Area 1
+ .
+ -- R2 ------ R5 -----
+ / .\ \
+ / . | \
+R1 --- R3 ------ R6 ------ R7
+ \ / |. |
+ \ / |. |
+ -- R4 ---- |. |
+ / ./
+ R8 --
+ .
+
+We check routes on R1, primarily those towards R7/8. Those to R7 are
+inter-area routes with R5/6 being ABRs, those to R8 are intra-area routes
+and are used for reference. R7/R8 announce one internal and one external
+route each.
With all links up, we expect 3 ECMP paths and 3 nexthops on R1 towards each
-of R6/7/8/9. Then we bring down the R2-R5 link, causing only 2 remaining
-paths and 2 nexthops on R1. The test is successful if the number of nexthops
-for the routes on R1 is as expected.
+of R7/8. Then we bring down the R3-R6 link, causing only 2 remaining
+paths and 2 nexthops on R1. Then we bring down the R2-R5 link, causing only
+1 remaining path and 1 nexthop on R1.
+
+The test is successful if the number of nexthops for the routes on R1 is as
+expected.
"""
import os
@@ -65,20 +72,24 @@ pytestmark = [pytest.mark.ospf6d]
def build_topo(tgen):
"Build function"
- # Create 9 routers
- for routern in range(1, 10):
+ # Create 8 routers
+ for routern in range(1, 9):
tgen.add_router("r{}".format(routern))
-
tgen.gears["r1"].add_link(tgen.gears["r2"])
tgen.gears["r1"].add_link(tgen.gears["r3"])
tgen.gears["r1"].add_link(tgen.gears["r4"])
tgen.gears["r2"].add_link(tgen.gears["r5"])
- tgen.gears["r3"].add_link(tgen.gears["r5"])
- tgen.gears["r4"].add_link(tgen.gears["r5"])
- tgen.gears["r5"].add_link(tgen.gears["r6"])
+ tgen.gears["r3"].add_link(tgen.gears["r6"])
+ tgen.gears["r4"].add_link(tgen.gears["r6"])
tgen.gears["r5"].add_link(tgen.gears["r7"])
tgen.gears["r5"].add_link(tgen.gears["r8"])
- tgen.gears["r5"].add_link(tgen.gears["r9"])
+ tgen.gears["r6"].add_link(tgen.gears["r7"])
+ tgen.gears["r6"].add_link(tgen.gears["r8"])
+ # Additional "loopback" interfaces. Not used for communication, just to
+ # hold an address we use to inject intra-/inter-area routes (the one on
+ # the real "lo" loopback is used for external routes).
+ tgen.gears["r7"].add_link(tgen.gears["r7"])
+ tgen.gears["r8"].add_link(tgen.gears["r8"])
def setup_module(mod):
@@ -131,20 +142,19 @@ def test_wait_protocol_convergence():
expect_neighbor_full("r2", "10.254.254.1")
expect_neighbor_full("r2", "10.254.254.5")
expect_neighbor_full("r3", "10.254.254.1")
- expect_neighbor_full("r3", "10.254.254.5")
+ expect_neighbor_full("r3", "10.254.254.6")
expect_neighbor_full("r4", "10.254.254.1")
- expect_neighbor_full("r4", "10.254.254.5")
+ expect_neighbor_full("r4", "10.254.254.6")
expect_neighbor_full("r5", "10.254.254.2")
- expect_neighbor_full("r5", "10.254.254.3")
- expect_neighbor_full("r5", "10.254.254.4")
- expect_neighbor_full("r5", "10.254.254.6")
expect_neighbor_full("r5", "10.254.254.7")
expect_neighbor_full("r5", "10.254.254.8")
- expect_neighbor_full("r5", "10.254.254.9")
- expect_neighbor_full("r6", "10.254.254.5")
+ expect_neighbor_full("r6", "10.254.254.3")
+ expect_neighbor_full("r6", "10.254.254.7")
+ expect_neighbor_full("r6", "10.254.254.8")
expect_neighbor_full("r7", "10.254.254.5")
+ expect_neighbor_full("r7", "10.254.254.6")
expect_neighbor_full("r8", "10.254.254.5")
- expect_neighbor_full("r9", "10.254.254.5")
+ expect_neighbor_full("r8", "10.254.254.6")
def test_ecmp_inter_area():
@@ -154,9 +164,16 @@ def test_ecmp_inter_area():
pytest.skip(tgen.errors)
def num_nexthops(router):
- routes = tgen.gears[router].vtysh_cmd("show ipv6 ospf6 route json", isjson=True)
- route_prefixes_infos = sorted(routes.get("routes", {}).items())
- return [len(ri.get("nextHops", [])) for rp, ri in route_prefixes_infos]
+ # Careful: "show ipv6 ospf6 route json" doesn't work here. It will
+ # only list one route type per prefix and that might not necessarily
+ # be the best/selected route. "show ipv6 route ospf6 json" only
+ # lists selected routes, so that's more useful in this case.
+ routes = tgen.gears[router].vtysh_cmd("show ipv6 route ospf6 json", isjson=True)
+ route_prefixes_infos = sorted(routes.items())
+ # Note: ri may contain one entry per routing protocol, but since
+ # we've explicitly requested only ospf6 above, we can count on ri[0]
+ # being the entry we're looking for.
+ return [ri[0]["internalNextHopActiveNum"] for rp, ri in route_prefixes_infos]
def expect_num_nexthops(router, expected_num_nexthops, count):
"Wait until number of nexthops for routes matches expectation"
@@ -174,14 +191,22 @@ def test_ecmp_inter_area():
), "'{}' wrong number of route nexthops".format(router)
# Check nexthops pre link-down
- expect_num_nexthops("r1", [1, 1, 1, 3, 3, 3, 3, 3, 3, 3], 4)
+ # tgen.mininet_cli()
+ expect_num_nexthops("r1", [1, 1, 1, 1, 2, 3, 3, 3, 3], 4)
+
+ logger.info("triggering R3-R6 link down")
+ tgen.gears["r3"].run("ip link set r3-eth1 down")
+
+ # tgen.mininet_cli()
+ # Check nexthops post link-down
+ expect_num_nexthops("r1", [1, 1, 1, 1, 1, 2, 2, 2, 2], 8)
- logger.info("triggering R2-R4 link down")
+ logger.info("triggering R2-R5 link down")
tgen.gears["r2"].run("ip link set r2-eth1 down")
# tgen.mininet_cli()
# Check nexthops post link-down
- expect_num_nexthops("r1", [1, 1, 1, 2, 2, 2, 2, 2, 2, 2], 8)
+ expect_num_nexthops("r1", [1, 1, 1, 1, 1, 1, 1, 1, 1], 8)
def teardown_module(_mod):
diff --git a/tools/gcc-plugins/frr-format.c b/tools/gcc-plugins/frr-format.c
index 4e2c2d3ba9..963741e479 100644
--- a/tools/gcc-plugins/frr-format.c
+++ b/tools/gcc-plugins/frr-format.c
@@ -66,6 +66,8 @@ static GTY(()) tree local_pid_t_node;
static GTY(()) tree local_uid_t_node;
static GTY(()) tree local_gid_t_node;
static GTY(()) tree local_time_t_node;
+static GTY(()) tree local_suseconds_t_node;
+static GTY(()) tree local_suseconds64_t_node;
static GTY(()) tree local_socklen_t_node;
static GTY(()) tree local_in_addr_t_node;
@@ -85,6 +87,8 @@ static struct type_special {
{ &local_uid_t_node, NULL, &local_uid_t_node, },
{ &local_gid_t_node, NULL, &local_gid_t_node, },
{ &local_time_t_node, NULL, &local_time_t_node, },
+ { &local_suseconds_t_node, NULL, &local_suseconds_t_node, },
+ { &local_suseconds64_t_node, NULL, &local_suseconds64_t_node, },
{ NULL, NULL, NULL, }
};
@@ -4176,6 +4180,8 @@ handle_finish_parse (void *event_data, void *data)
setup_type ("uid_t", &local_uid_t_node);
setup_type ("gid_t", &local_gid_t_node);
setup_type ("time_t", &local_time_t_node);
+ setup_type ("__suseconds_t", &local_suseconds_t_node);
+ setup_type ("__suseconds64_t", &local_suseconds64_t_node);
setup_type ("socklen_t", &local_socklen_t_node);
setup_type ("in_addr_t", &local_in_addr_t_node);
diff --git a/yang/frr-test-module.yang b/yang/frr-test-module.yang
index 6cc60e8665..dcf204a956 100644
--- a/yang/frr-test-module.yang
+++ b/yang/frr-test-module.yang
@@ -80,6 +80,23 @@ module frr-test-module {
}
}
}
+ action ping {
+ input {
+ leaf data {
+ type string;
+ }
+ }
+ output {
+ leaf vrf {
+ type string;
+ }
+ // can't use the same name in input and output
+ // because of a bug in libyang < 2.1.148
+ leaf data-out {
+ type string;
+ }
+ }
+ }
}
}
choice achoice {
diff --git a/zebra/fpm_listener.c b/zebra/fpm_listener.c
index b31c5f7ac6..5533fa7f8b 100644
--- a/zebra/fpm_listener.c
+++ b/zebra/fpm_listener.c
@@ -36,6 +36,8 @@
#include "fpm/fpm.h"
#include "lib/libfrr.h"
+XREF_SETUP();
+
struct glob {
int server_sock;
int sock;
diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c
index 0d9d912f83..5c19d226b1 100644
--- a/zebra/zebra_evpn_mh.c
+++ b/zebra/zebra_evpn_mh.c
@@ -563,8 +563,9 @@ zebra_evpn_acc_vl_new(vlanid_t vid, struct interface *br_if)
struct zebra_evpn_access_bd *acc_bd;
struct interface *vlan_if;
- if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
- zlog_debug("access vlan %d bridge %s add", vid, br_if->name);
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s access vlan %d bridge %s add", __func__, vid,
+ br_if->name);
acc_bd = XCALLOC(MTYPE_ZACC_BD, sizeof(struct zebra_evpn_access_bd));
@@ -582,8 +583,8 @@ zebra_evpn_acc_vl_new(vlanid_t vid, struct interface *br_if)
vlan_if = zvni_map_to_svi(vid, br_if);
if (vlan_if) {
if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
- zlog_debug("vlan %d bridge %s SVI %s set", vid,
- br_if->name, vlan_if->name);
+ zlog_debug("%s vlan %d bridge %s SVI %s set", __func__,
+ vid, br_if->name, vlan_if->name);
acc_bd->vlan_zif = vlan_if->info;
}
return acc_bd;
@@ -731,6 +732,29 @@ static void zebra_evpn_acc_bd_evpn_set(struct zebra_evpn_access_bd *acc_bd,
}
}
+/* Lookup API for VxLAN_IF's Bridge, VLAN in EVPN cache */
+int zebra_evpn_vl_vxl_bridge_lookup(uint16_t vid, struct zebra_if *vxlan_zif)
+{
+ struct interface *br_if;
+ struct zebra_evpn_access_bd *acc_bd;
+
+ if (!vid)
+ return -1;
+
+ br_if = vxlan_zif->brslave_info.br_if;
+
+ if (!br_if)
+ return -1;
+
+ acc_bd = zebra_evpn_acc_vl_find(vid, br_if);
+
+ if (!acc_bd)
+ return 0;
+
+ return 1;
+}
+
+
/* handle VLAN->VxLAN_IF association */
void zebra_evpn_vl_vxl_ref(uint16_t vid, vni_t vni_id,
struct zebra_if *vxlan_zif)
@@ -768,8 +792,9 @@ void zebra_evpn_vl_vxl_ref(uint16_t vid, vni_t vni_id,
if (acc_bd->zevpn == old_zevpn)
return;
- if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
- zlog_debug("access vlan %d vni %u ref", acc_bd->vid, vni_id);
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s bridge %s access vlan %d vni %u ref", __func__,
+ br_if->name, acc_bd->vid, vni_id);
if (old_zevpn)
zebra_evpn_acc_bd_evpn_set(acc_bd, NULL, old_zevpn);
diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h
index 34ef79f155..f68e2eae60 100644
--- a/zebra/zebra_evpn_mh.h
+++ b/zebra/zebra_evpn_mh.h
@@ -378,7 +378,8 @@ extern void zebra_evpn_es_bypass_update(struct zebra_evpn_es *es,
extern void zebra_evpn_proc_remote_nh(ZAPI_HANDLER_ARGS);
extern struct zebra_evpn_es_evi *
zebra_evpn_es_evi_find(struct zebra_evpn_es *es, struct zebra_evpn *zevpn);
-
+extern int zebra_evpn_vl_vxl_bridge_lookup(uint16_t vid,
+ struct zebra_if *vxlan_zif);
void zebra_build_type3_esi(uint32_t lid, struct ethaddr *mac, esi_t *esi);
void zebra_evpn_es_sys_mac_update(struct zebra_if *zif, struct ethaddr *sysmac);
diff --git a/zebra/zebra_l2.c b/zebra/zebra_l2.c
index 0f591810b9..240f674b0f 100644
--- a/zebra/zebra_l2.c
+++ b/zebra/zebra_l2.c
@@ -384,7 +384,7 @@ void zebra_l2_vxlanif_update_access_vlan(struct interface *ifp,
return;
old_access_vlan = zif->l2info.vxl.vni_info.vni.access_vlan;
- ;
+
if (old_access_vlan == access_vlan)
return;
diff --git a/zebra/zebra_nb_rpcs.c b/zebra/zebra_nb_rpcs.c
index 083ab3fde6..938193df2f 100644
--- a/zebra/zebra_nb_rpcs.c
+++ b/zebra/zebra_nb_rpcs.c
@@ -20,48 +20,30 @@ int clear_evpn_dup_addr_rpc(struct nb_cb_rpc_args *args)
{
struct zebra_vrf *zvrf;
int ret = NB_OK;
- struct yang_data *yang_dup_choice = NULL, *yang_dup_vni = NULL,
- *yang_dup_ip = NULL, *yang_dup_mac = NULL;
-
- yang_dup_choice = yang_data_list_find(args->input, "%s/%s", args->xpath,
- "input/clear-dup-choice");
zvrf = zebra_vrf_get_evpn();
- if (yang_dup_choice
- && strcmp(yang_dup_choice->value, "all-case") == 0) {
+ if (yang_dnode_exists(args->input, "all-vnis")) {
zebra_vxlan_clear_dup_detect_vni_all(zvrf);
} else {
- vni_t vni;
+ vni_t vni = yang_dnode_get_uint32(args->input, "vni-id");
struct ipaddr host_ip = {.ipa_type = IPADDR_NONE};
struct ethaddr mac;
- yang_dup_vni = yang_data_list_find(
- args->input, "%s/%s", args->xpath,
- "input/clear-dup-choice/single-case/vni-id");
- if (yang_dup_vni) {
- vni = yang_str2uint32(yang_dup_vni->value);
-
- yang_dup_mac = yang_data_list_find(
- args->input, "%s/%s", args->xpath,
- "input/clear-dup-choice/single-case/vni-id/mac-addr");
- yang_dup_ip = yang_data_list_find(
- args->input, "%s/%s", args->xpath,
- "input/clear-dup-choice/single-case/vni-id/vni-ipaddr");
-
- if (yang_dup_mac) {
- yang_str2mac(yang_dup_mac->value, &mac);
- ret = zebra_vxlan_clear_dup_detect_vni_mac(
- zvrf, vni, &mac, args->errmsg,
- args->errmsg_len);
- } else if (yang_dup_ip) {
- yang_str2ip(yang_dup_ip->value, &host_ip);
- ret = zebra_vxlan_clear_dup_detect_vni_ip(
- zvrf, vni, &host_ip, args->errmsg,
- args->errmsg_len);
- } else
- ret = zebra_vxlan_clear_dup_detect_vni(zvrf,
- vni);
+ if (yang_dnode_exists(args->input, "mac-addr")) {
+ yang_dnode_get_mac(&mac, args->input, "mac-addr");
+ ret = zebra_vxlan_clear_dup_detect_vni_mac(zvrf, vni,
+ &mac,
+ args->errmsg,
+ args->errmsg_len);
+ } else if (yang_dnode_exists(args->input, "vni-ipaddr")) {
+ yang_dnode_get_ip(&host_ip, args->input, "vni-ipaddr");
+ ret = zebra_vxlan_clear_dup_detect_vni_ip(zvrf, vni,
+ &host_ip,
+ args->errmsg,
+ args->errmsg_len);
+ } else {
+ ret = zebra_vxlan_clear_dup_detect_vni(zvrf, vni);
}
}
if (ret < 0)
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index 0b5362094e..bdbb059e56 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -3553,56 +3553,17 @@ DEFPY (clear_evpn_dup_addr,
"IPv4 address\n"
"IPv6 address\n")
{
- struct ipaddr host_ip = {.ipa_type = IPADDR_NONE };
- int ret = CMD_SUCCESS;
- struct list *input;
- struct yang_data *yang_dup = NULL, *yang_dup_ip = NULL,
- *yang_dup_mac = NULL;
-
- input = list_new();
-
if (!vni_str) {
- yang_dup = yang_data_new(
- "/frr-zebra:clear-evpn-dup-addr/input/clear-dup-choice",
- "all-case");
+ nb_cli_rpc_enqueue(vty, "all-vnis", NULL);
} else {
- yang_dup = yang_data_new_uint32(
- "/frr-zebra:clear-evpn-dup-addr/input/clear-dup-choice/single-case/vni-id",
- vni);
- if (!is_zero_mac(&mac->eth_addr)) {
- yang_dup_mac = yang_data_new_mac(
- "/frr-zebra:clear-evpn-dup-addr/input/clear-dup-choice/single-case/vni-id/mac-addr",
- &mac->eth_addr);
- if (yang_dup_mac)
- listnode_add(input, yang_dup_mac);
- } else if (ip) {
- if (sockunion_family(ip) == AF_INET) {
- host_ip.ipa_type = IPADDR_V4;
- host_ip.ipaddr_v4.s_addr = sockunion2ip(ip);
- } else {
- host_ip.ipa_type = IPADDR_V6;
- memcpy(&host_ip.ipaddr_v6, &ip->sin6.sin6_addr,
- sizeof(struct in6_addr));
- }
-
- yang_dup_ip = yang_data_new_ip(
- "/frr-zebra:clear-evpn-dup-addr/input/clear-dup-choice/single-case/vni-id/vni-ipaddr",
- &host_ip);
-
- if (yang_dup_ip)
- listnode_add(input, yang_dup_ip);
- }
- }
-
- if (yang_dup) {
- listnode_add(input, yang_dup);
- ret = nb_cli_rpc(vty, "/frr-zebra:clear-evpn-dup-addr", input,
- NULL);
+ nb_cli_rpc_enqueue(vty, "vni-id", vni_str);
+ if (mac_str)
+ nb_cli_rpc_enqueue(vty, "mac-addr", mac_str);
+ else if (ip_str)
+ nb_cli_rpc_enqueue(vty, "vni-ipaddr", ip_str);
}
- list_delete(&input);
-
- return ret;
+ return nb_cli_rpc(vty, "/frr-zebra:clear-evpn-dup-addr", NULL);
}
DEFPY_HIDDEN (evpn_accept_bgp_seq,
diff --git a/zebra/zebra_vxlan_if.c b/zebra/zebra_vxlan_if.c
index 3cc7e499bf..f4b859b861 100644
--- a/zebra/zebra_vxlan_if.c
+++ b/zebra/zebra_vxlan_if.c
@@ -506,7 +506,7 @@ static int zebra_vxlan_if_add_update_vni(struct zebra_if *zif,
if ((hashcount(ctx->old_vni_table) == 0) ||
!(old_vni = hash_release(ctx->old_vni_table, &vni_tmp))) {
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("vxlan %s adding vni(%d, %d)",
+ zlog_debug("%s vxlan %s adding vni(%d, %d)", __func__,
zif->ifp->name, vni->vni, vni->access_vlan);
zebra_vxlan_if_vni_entry_add(zif, &vni_tmp);
@@ -521,17 +521,39 @@ static int zebra_vxlan_if_add_update_vni(struct zebra_if *zif,
if (old_vni->access_vlan != vni->access_vlan) {
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "vxlan %s updating vni(%d, %d) -> vni(%d, %d)",
- zif->ifp->name, old_vni->vni,
- old_vni->access_vlan, vni->vni,
- vni->access_vlan);
+ zlog_debug("%s vxlan %s updating vni(%d, %d) -> vni(%d, %d)",
+ __func__, zif->ifp->name, old_vni->vni,
+ old_vni->access_vlan, vni->vni,
+ vni->access_vlan);
zebra_evpn_vl_vxl_deref(old_vni->access_vlan, old_vni->vni,
zif);
zebra_evpn_vl_vxl_ref(vni->access_vlan, vni->vni, zif);
zebra_vxlan_if_update_vni(zif->ifp, vni, ctx);
zebra_vxlan_vni_free(old_vni);
+ } else {
+ int ret;
+
+ ret = zebra_evpn_vl_vxl_bridge_lookup(vni->access_vlan, zif);
+ /* Here ret value 0 implied bridge vlan mapping is not present
+ * repopulated. Ignore ret value 1 as it means vlan mapping is
+ * present in bridge table.
+ */
+ if (ret < 0) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("%s vxlan %s vni %u has error accessing bridge table.",
+ __func__, zif->ifp->name, vni->vni);
+ } else if (ret == 0) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("%s vxlan %s vni (%u, %u) not present in bridge table",
+ __func__, zif->ifp->name, vni->vni,
+ vni->access_vlan);
+ zebra_evpn_vl_vxl_deref(old_vni->access_vlan,
+ old_vni->vni, zif);
+ zebra_evpn_vl_vxl_ref(vni->access_vlan, vni->vni, zif);
+ zebra_vxlan_if_update_vni(zif->ifp, vni, ctx);
+ zebra_vxlan_vni_free(old_vni);
+ }
}
return 0;
@@ -768,6 +790,7 @@ vni_t zebra_vxlan_if_access_vlan_vni_find(struct zebra_if *zif,
return vni->vni;
}
+/* SVD VLAN-VNI mapping update */
int zebra_vxlan_if_vni_table_add_update(struct interface *ifp,
struct hash *vni_table)
{