diff options
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 */ @@ -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)); @@ -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) { @@ -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)) @@ -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) { |
