summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.dir-locals.el8
-rw-r--r--README.md4
-rw-r--r--bfdd/bfd.c47
-rw-r--r--bfdd/bfd.h35
-rw-r--r--bfdd/bfd_packet.c3
-rw-r--r--bfdd/bfdd.c1
-rw-r--r--bfdd/bfdd_cli.c1
-rw-r--r--bfdd/bfdd_nb.c388
-rw-r--r--bfdd/bfdd_nb.h217
-rw-r--r--bfdd/bfdd_nb_config.c520
-rw-r--r--bfdd/bfdd_nb_state.c383
-rw-r--r--bfdd/bfdd_northbound.c1236
-rw-r--r--bfdd/control.c4
-rw-r--r--bfdd/ptm_adapter.c13
-rw-r--r--bfdd/subdir.am5
-rw-r--r--bgpd/bgp_bfd.c3
-rw-r--r--bgpd/bgp_debug.c14
-rw-r--r--bgpd/bgp_evpn.c7
-rw-r--r--bgpd/bgp_mpath.c2
-rw-r--r--bgpd/bgp_route.c37
-rw-r--r--bgpd/bgp_vty.c50
-rw-r--r--bgpd/bgp_zebra.c2
-rw-r--r--bgpd/bgpd.c26
-rw-r--r--bgpd/bgpd.h8
-rw-r--r--bgpd/rfapi/rfapi.c8
-rw-r--r--bgpd/rfapi/rfapi_import.c4
-rw-r--r--bgpd/rfapi/rfapi_vty.c2
-rw-r--r--bgpd/rfapi/vnc_import_bgp.c14
-rw-r--r--debian/copyright5
-rw-r--r--doc/developer/frr-release-procedure.rst181
-rw-r--r--doc/developer/maintainer-release-build.rst89
-rw-r--r--doc/developer/packaging.rst8
-rw-r--r--doc/developer/release-announcement-template.md40
-rw-r--r--doc/developer/subdir.am3
-rw-r--r--doc/developer/testing.rst1
-rw-r--r--doc/developer/topotests-jsontopo.rst671
-rw-r--r--doc/developer/workflow.rst31
-rw-r--r--doc/user/bgp.rst17
-rw-r--r--doc/user/filter.rst22
-rw-r--r--doc/user/overview.rst2
-rw-r--r--doc/user/vrrp.rst4
-rw-r--r--doc/user/zebra.rst24
-rw-r--r--eigrpd/eigrp_network.c3
-rw-r--r--eigrpd/eigrp_topology.c2
-rw-r--r--eigrpd/eigrp_zebra.c2
-rw-r--r--include/linux/nexthop.h58
-rw-r--r--include/subdir.am1
-rw-r--r--isisd/isis_adjacency.c15
-rw-r--r--isisd/isis_adjacency.h1
-rw-r--r--isisd/isis_bfd.c3
-rw-r--r--isisd/isis_circuit.c11
-rw-r--r--isisd/isis_cli.c2
-rw-r--r--isisd/isis_cli.h127
-rw-r--r--isisd/isis_lsp.c1
-rw-r--r--isisd/isis_main.c1
-rw-r--r--isisd/isis_nb.c815
-rw-r--r--isisd/isis_nb.h548
-rw-r--r--isisd/isis_nb_config.c2236
-rw-r--r--isisd/isis_nb_notifications.c491
-rw-r--r--isisd/isis_nb_state.c386
-rw-r--r--isisd/isis_northbound.c3953
-rw-r--r--isisd/isis_pdu.c1
-rw-r--r--isisd/isis_zebra.c2
-rw-r--r--isisd/isisd.c1
-rw-r--r--isisd/isisd.h46
-rw-r--r--isisd/subdir.am7
-rw-r--r--ldpd/ldp_zebra.c2
-rw-r--r--lib/bfd.c2
-rw-r--r--lib/bfd.h14
-rw-r--r--lib/if.c74
-rw-r--r--lib/if.h5
-rw-r--r--lib/nexthop.c62
-rw-r--r--lib/nexthop.h12
-rw-r--r--lib/nexthop_group.c130
-rw-r--r--lib/nexthop_group.h17
-rw-r--r--lib/prefix.c20
-rw-r--r--lib/prefix.h10
-rw-r--r--lib/printf/glue.c6
-rw-r--r--lib/route_types.txt2
-rw-r--r--lib/zebra.h5
-rw-r--r--nhrpd/nhrp_interface.c2
-rw-r--r--nhrpd/nhrp_vty.c5
-rw-r--r--ospf6d/ospf6_bfd.c2
-rw-r--r--ospf6d/ospf6_zebra.c2
-rw-r--r--ospfd/ospf_bfd.c28
-rw-r--r--ospfd/ospf_interface.c3
-rw-r--r--ospfd/ospf_zebra.c4
-rw-r--r--pbrd/pbr_nht.c3
-rw-r--r--pbrd/pbr_vty.c12
-rw-r--r--pbrd/pbr_zebra.c2
-rw-r--r--pimd/pim_bfd.c2
-rw-r--r--pimd/pim_bsm.c15
-rw-r--r--pimd/pim_cmd.c10
-rw-r--r--pimd/pim_ifchannel.c12
-rw-r--r--pimd/pim_neighbor.c2
-rw-r--r--pimd/pim_rp.c5
-rw-r--r--pimd/pim_tlv.c3
-rw-r--r--pimd/pim_zebra.c2
-rw-r--r--ripd/rip_cli.c2
-rw-r--r--ripd/rip_cli.h86
-rw-r--r--ripd/rip_interface.c2
-rw-r--r--ripd/rip_main.c1
-rw-r--r--ripd/rip_nb.c369
-rw-r--r--ripd/rip_nb.h265
-rw-r--r--ripd/rip_nb_config.c1099
-rw-r--r--ripd/rip_nb_notifications.c73
-rw-r--r--ripd/rip_nb_rpcs.c105
-rw-r--r--ripd/rip_nb_state.c284
-rw-r--r--ripd/rip_northbound.c1823
-rw-r--r--ripd/ripd.c1
-rw-r--r--ripd/ripd.h9
-rw-r--r--ripd/subdir.am8
-rw-r--r--ripngd/ripng_cli.c2
-rw-r--r--ripngd/ripng_cli.h58
-rw-r--r--ripngd/ripng_interface.c2
-rw-r--r--ripngd/ripng_main.c1
-rw-r--r--ripngd/ripng_nb.c252
-rw-r--r--ripngd/ripng_nb.h179
-rw-r--r--ripngd/ripng_nb_config.c714
-rw-r--r--ripngd/ripng_nb_rpcs.c107
-rw-r--r--ripngd/ripng_nb_state.c236
-rw-r--r--ripngd/ripng_northbound.c1234
-rw-r--r--ripngd/ripngd.h3
-rw-r--r--ripngd/subdir.am7
-rw-r--r--sharpd/sharp_zebra.c2
-rw-r--r--staticd/static_routes.c4
-rw-r--r--staticd/static_zebra.c4
-rw-r--r--tests/topotests/Dockerfile20
-rw-r--r--tests/topotests/bfd-topo2/r1/ipv6_routes.json1
-rw-r--r--tests/topotests/bfd-topo2/r2/ipv4_routes.json1
-rw-r--r--tests/topotests/bfd-topo2/r2/ipv6_routes.json1
-rw-r--r--tests/topotests/bfd-topo2/r3/ipv4_routes.json1
-rw-r--r--tests/topotests/bfd-topo2/r4/ipv6_routes.json1
-rw-r--r--tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json1
-rw-r--r--tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json1
-rw-r--r--tests/topotests/ldp-vpls-topo1/r1/zebra.conf3
-rw-r--r--tests/topotests/ldp-vpls-topo1/r2/zebra.conf3
-rw-r--r--tests/topotests/ldp-vpls-topo1/r3/zebra.conf3
-rwxr-xr-xtests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py1
-rw-r--r--tests/topotests/lib/topogen.py5
-rw-r--r--tools/gen_northbound_callbacks.c55
-rwxr-xr-xtools/tarsource.sh9
-rw-r--r--vrrpd/vrrp.c36
-rw-r--r--zebra/connected.c16
-rw-r--r--zebra/if_netlink.c25
-rw-r--r--zebra/interface.c99
-rw-r--r--zebra/interface.h18
-rw-r--r--zebra/kernel_netlink.c33
-rw-r--r--zebra/kernel_socket.c11
-rw-r--r--zebra/main.c1
-rw-r--r--zebra/redistribute.c8
-rw-r--r--zebra/rib.h42
-rw-r--r--zebra/rt.h8
-rw-r--r--zebra/rt_netlink.c1094
-rw-r--r--zebra/rt_netlink.h4
-rw-r--r--zebra/rt_socket.c5
-rw-r--r--zebra/rtread_getmsg.c2
-rw-r--r--zebra/subdir.am1
-rw-r--r--zebra/zapi_msg.c28
-rw-r--r--zebra/zebra_dplane.c295
-rw-r--r--zebra/zebra_dplane.h27
-rw-r--r--zebra/zebra_errors.c51
-rw-r--r--zebra/zebra_errors.h6
-rw-r--r--zebra/zebra_fpm_dt.c2
-rw-r--r--zebra/zebra_fpm_netlink.c2
-rw-r--r--zebra/zebra_fpm_protobuf.c2
-rw-r--r--zebra/zebra_mpls.c61
-rw-r--r--zebra/zebra_nhg.c1406
-rw-r--r--zebra/zebra_nhg.h199
-rw-r--r--zebra/zebra_nhg_private.h62
-rw-r--r--zebra/zebra_pw.c2
-rw-r--r--zebra/zebra_rib.c487
-rw-r--r--zebra/zebra_rnh.c22
-rw-r--r--zebra/zebra_router.c19
-rw-r--r--zebra/zebra_router.h8
-rw-r--r--zebra/zebra_snmp.c12
-rw-r--r--zebra/zebra_vty.c327
-rw-r--r--zebra/zebra_vxlan.c14
178 files changed, 14878 insertions, 9923 deletions
diff --git a/.dir-locals.el b/.dir-locals.el
index 21392ecf28..e47f245db7 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -2,7 +2,7 @@
;;; For more information see (info "(emacs) Directory Variables")
;;; Match project coding conventions
-((c-mode
- (indent-tabs-mode . t)
- (show-trailing-whitespace . t)
- (c-basic-offset . 8)))
+((c-mode . ((indent-tabs-mode . t)
+ (show-trailing-whitespace . t)
+ (c-basic-offset . 8)
+ )))
diff --git a/README.md b/README.md
index bb13481cf2..937728c9e6 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,7 @@
+<p align="center">
+<img src="http://docs.frrouting.org/en/latest/_static/frr-icon.svg" alt="Icon" width="20%"/>
+</p>
+
FRRouting
=========
diff --git a/bfdd/bfd.c b/bfdd/bfd.c
index 90287114fc..6e956aa929 100644
--- a/bfdd/bfd.c
+++ b/bfdd/bfd.c
@@ -49,6 +49,8 @@ static void bs_admin_down_handler(struct bfd_session *bs, int nstate);
static void bs_down_handler(struct bfd_session *bs, int nstate);
static void bs_init_handler(struct bfd_session *bs, int nstate);
static void bs_up_handler(struct bfd_session *bs, int nstate);
+static void bs_neighbour_admin_down_handler(struct bfd_session *bfd,
+ uint8_t diag);
/* Zeroed array with the size of an IPv6 address. */
struct in6_addr zero_addr;
@@ -159,6 +161,7 @@ int bfd_session_enable(struct bfd_session *bs)
bs->vrf = vrf;
if (bs->vrf == NULL)
bs->vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ assert(bs->vrf);
if (bs->key.ifname[0]
&& BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) == 0)
@@ -311,7 +314,7 @@ void ptm_bfd_sess_up(struct bfd_session *bfd)
/* Start sending control packets with poll bit immediately. */
ptm_bfd_snd(bfd, 0);
- control_notify(bfd);
+ control_notify(bfd, bfd->ses_state);
if (old_state != bfd->ses_state) {
bfd->stats.session_up++;
@@ -346,7 +349,7 @@ void ptm_bfd_sess_dn(struct bfd_session *bfd, uint8_t diag)
/* only signal clients when going from up->down state */
if (old_state == PTM_BFD_UP)
- control_notify(bfd);
+ control_notify(bfd, PTM_BFD_DOWN);
/* Stop echo packet transmission if they are active */
if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
@@ -581,7 +584,7 @@ skip_echo:
/* Change and notify state change. */
bs->ses_state = PTM_BFD_ADM_DOWN;
- control_notify(bs);
+ control_notify(bs, bs->ses_state);
/* Don't try to send packets with a disabled session. */
if (bs->sock != -1)
@@ -595,7 +598,7 @@ skip_echo:
/* Change and notify state change. */
bs->ses_state = PTM_BFD_DOWN;
- control_notify(bs);
+ control_notify(bs, bs->ses_state);
/* Enable all timers. */
bfd_recvtimer_update(bs);
@@ -867,10 +870,46 @@ static void bs_init_handler(struct bfd_session *bs, int nstate)
}
}
+static void bs_neighbour_admin_down_handler(struct bfd_session *bfd,
+ uint8_t diag)
+{
+ int old_state = bfd->ses_state;
+
+ bfd->local_diag = diag;
+ bfd->discrs.remote_discr = 0;
+ bfd->ses_state = PTM_BFD_DOWN;
+ bfd->polling = 0;
+ bfd->demand_mode = 0;
+ monotime(&bfd->downtime);
+
+ /* Slow down the control packets, the connection is down. */
+ bs_set_slow_timers(bfd);
+
+ /* only signal clients when going from up->down state */
+ if (old_state == PTM_BFD_UP)
+ control_notify(bfd, PTM_BFD_ADM_DOWN);
+
+ /* Stop echo packet transmission if they are active */
+ if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
+ ptm_bfd_echo_stop(bfd);
+
+ if (old_state != bfd->ses_state) {
+ bfd->stats.session_down++;
+
+ log_info("state-change: [%s] %s -> %s reason:%s",
+ bs_to_string(bfd), state_list[old_state].str,
+ state_list[bfd->ses_state].str,
+ get_diag_str(bfd->local_diag));
+ }
+}
+
static void bs_up_handler(struct bfd_session *bs, int nstate)
{
switch (nstate) {
case PTM_BFD_ADM_DOWN:
+ bs_neighbour_admin_down_handler(bs, BD_ADMIN_DOWN);
+ break;
+
case PTM_BFD_DOWN:
/* Peer lost or asked to shutdown connection. */
ptm_bfd_sess_dn(bs, BD_NEIGHBOR_DOWN);
diff --git a/bfdd/bfd.h b/bfdd/bfd.h
index a9c8bd183a..220dd6e0a8 100644
--- a/bfdd/bfd.h
+++ b/bfdd/bfd.h
@@ -365,7 +365,7 @@ TAILQ_HEAD(bcslist, bfd_control_socket);
int control_init(const char *path);
void control_shutdown(void);
-int control_notify(struct bfd_session *bs);
+int control_notify(struct bfd_session *bs, uint8_t notify_state);
int control_notify_config(const char *op, struct bfd_session *bs);
int control_accept(struct thread *t);
@@ -593,29 +593,6 @@ void bfdd_vty_init(void);
*/
void bfdd_cli_init(void);
-void bfd_cli_show_header(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void bfd_cli_show_header_end(struct vty *vty, struct lyd_node *dnode);
-void bfd_cli_show_single_hop_peer(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-void bfd_cli_show_multi_hop_peer(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-void bfd_cli_show_peer_end(struct vty *vty, struct lyd_node *dnode);
-void bfd_cli_show_mult(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void bfd_cli_show_tx(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void bfd_cli_show_rx(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void bfd_cli_show_shutdown(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void bfd_cli_show_echo(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void bfd_cli_show_echo_interval(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-
/*
* ptm_adapter.c
@@ -628,14 +605,6 @@ void bfdd_sessions_enable_vrf(struct vrf *vrf);
void bfdd_sessions_disable_vrf(struct vrf *vrf);
void bfd_session_update_vrf_name(struct bfd_session *bs, struct vrf *vrf);
-int ptm_bfd_notify(struct bfd_session *bs);
-
-
-/*
- * bfdd_northbound.c
- *
- * BFD northbound callbacks.
- */
-extern const struct frr_yang_module_info frr_bfdd_info;
+int ptm_bfd_notify(struct bfd_session *bs, uint8_t notify_state);
#endif /* _BFD_H_ */
diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c
index 7fbe6db163..ed36bb742e 100644
--- a/bfdd/bfd_packet.c
+++ b/bfdd/bfd_packet.c
@@ -529,8 +529,7 @@ int bfd_recv_cb(struct thread *t)
uint8_t msgbuf[1516];
struct bfd_vrf_global *bvrf = THREAD_ARG(t);
- if (bvrf)
- vrfid = bvrf->vrf->vrf_id;
+ vrfid = bvrf->vrf->vrf_id;
/* Schedule next read. */
bfd_sd_reschedule(bvrf, sd);
diff --git a/bfdd/bfdd.c b/bfdd/bfdd.c
index 5657744f75..a91fa3d047 100644
--- a/bfdd/bfdd.c
+++ b/bfdd/bfdd.c
@@ -23,6 +23,7 @@
#include "filter.h"
#include "bfd.h"
+#include "bfdd_nb.h"
#include "lib/version.h"
diff --git a/bfdd/bfdd_cli.c b/bfdd/bfdd_cli.c
index acb1801cc4..3e44fcb22a 100644
--- a/bfdd/bfdd_cli.c
+++ b/bfdd/bfdd_cli.c
@@ -31,6 +31,7 @@
#endif /* VTYSH_EXTRACT_PL */
#include "bfd.h"
+#include "bfdd_nb.h"
/*
* Definitions.
diff --git a/bfdd/bfdd_nb.c b/bfdd/bfdd_nb.c
new file mode 100644
index 0000000000..8d46742337
--- /dev/null
+++ b/bfdd/bfdd_nb.c
@@ -0,0 +1,388 @@
+/*
+ * BFD daemon northbound implementation.
+ *
+ * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
+ * Rafael Zalamena
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA.
+ */
+
+#include <zebra.h>
+
+#include "lib/log.h"
+#include "lib/northbound.h"
+
+#include "bfdd_nb.h"
+
+/* clang-format off */
+const struct frr_yang_module_info frr_bfdd_info = {
+ .name = "frr-bfdd",
+ .nodes = {
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd",
+ .cbs = {
+ .create = bfdd_bfd_create,
+ .destroy = bfdd_bfd_destroy,
+ .cli_show = bfd_cli_show_header,
+ .cli_show_end = bfd_cli_show_header_end,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop",
+ .cbs = {
+ .create = bfdd_bfd_sessions_single_hop_create,
+ .destroy = bfdd_bfd_sessions_single_hop_destroy,
+ .get_next = bfdd_bfd_sessions_single_hop_get_next,
+ .get_keys = bfdd_bfd_sessions_single_hop_get_keys,
+ .lookup_entry = bfdd_bfd_sessions_single_hop_lookup_entry,
+ .cli_show = bfd_cli_show_single_hop_peer,
+ .cli_show_end = bfd_cli_show_peer_end,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/source-addr",
+ .cbs = {
+ .modify = bfdd_bfd_sessions_single_hop_source_addr_modify,
+ .destroy = bfdd_bfd_sessions_single_hop_source_addr_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/detection-multiplier",
+ .cbs = {
+ .modify = bfdd_bfd_sessions_single_hop_detection_multiplier_modify,
+ .cli_show = bfd_cli_show_mult,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/desired-transmission-interval",
+ .cbs = {
+ .modify = bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify,
+ .cli_show = bfd_cli_show_tx,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/required-receive-interval",
+ .cbs = {
+ .modify = bfdd_bfd_sessions_single_hop_required_receive_interval_modify,
+ .cli_show = bfd_cli_show_rx,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/administrative-down",
+ .cbs = {
+ .modify = bfdd_bfd_sessions_single_hop_administrative_down_modify,
+ .cli_show = bfd_cli_show_shutdown,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/echo-mode",
+ .cbs = {
+ .modify = bfdd_bfd_sessions_single_hop_echo_mode_modify,
+ .cli_show = bfd_cli_show_echo,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/desired-echo-transmission-interval",
+ .cbs = {
+ .modify = bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify,
+ .cli_show = bfd_cli_show_echo_interval,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-discriminator",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-state",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_local_state_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-diagnostic",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_local_diagnostic_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-multiplier",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_local_multiplier_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-discriminator",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_discriminator_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-state",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_state_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-diagnostic",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_diagnostic_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-multiplier",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_multiplier_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-transmission-interval",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_transmission_interval_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-receive-interval",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_receive_interval_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/detection-mode",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_detection_mode_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/last-down-time",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_last_down_time_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/last-up-time",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_last_up_time_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/session-down-count",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_session_down_count_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/session-up-count",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_session_up_count_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/control-packet-input-count",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_input_count_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/control-packet-output-count",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_output_count_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-echo-transmission-interval",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_echo_transmission_interval_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/echo-packet-input-count",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_input_count_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/echo-packet-output-count",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop",
+ .cbs = {
+ .create = bfdd_bfd_sessions_multi_hop_create,
+ .destroy = bfdd_bfd_sessions_multi_hop_destroy,
+ .get_next = bfdd_bfd_sessions_multi_hop_get_next,
+ .get_keys = bfdd_bfd_sessions_multi_hop_get_keys,
+ .lookup_entry = bfdd_bfd_sessions_multi_hop_lookup_entry,
+ .cli_show = bfd_cli_show_multi_hop_peer,
+ .cli_show_end = bfd_cli_show_peer_end,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/detection-multiplier",
+ .cbs = {
+ .modify = bfdd_bfd_sessions_single_hop_detection_multiplier_modify,
+ .cli_show = bfd_cli_show_mult,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/desired-transmission-interval",
+ .cbs = {
+ .modify = bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify,
+ .cli_show = bfd_cli_show_tx,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/required-receive-interval",
+ .cbs = {
+ .modify = bfdd_bfd_sessions_single_hop_required_receive_interval_modify,
+ .cli_show = bfd_cli_show_rx,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/administrative-down",
+ .cbs = {
+ .modify = bfdd_bfd_sessions_single_hop_administrative_down_modify,
+ .cli_show = bfd_cli_show_shutdown,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/local-discriminator",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/local-state",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_local_state_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/local-diagnostic",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_local_diagnostic_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/local-multiplier",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_local_multiplier_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/remote-discriminator",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_discriminator_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/remote-state",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_state_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/remote-diagnostic",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_diagnostic_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/remote-multiplier",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_multiplier_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/negotiated-transmission-interval",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_transmission_interval_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/negotiated-receive-interval",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_receive_interval_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/detection-mode",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_detection_mode_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/last-down-time",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_last_down_time_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/last-up-time",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_last_up_time_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/session-down-count",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_session_down_count_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/session-up-count",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_session_up_count_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/control-packet-input-count",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_input_count_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/control-packet-output-count",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_output_count_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/negotiated-echo-transmission-interval",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_echo_transmission_interval_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/echo-packet-input-count",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_input_count_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/echo-packet-output-count",
+ .cbs = {
+ .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem,
+ }
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
diff --git a/bfdd/bfdd_nb.h b/bfdd/bfdd_nb.h
new file mode 100644
index 0000000000..8377c87bb7
--- /dev/null
+++ b/bfdd/bfdd_nb.h
@@ -0,0 +1,217 @@
+/*
+ * BFD daemon northbound implementation.
+ *
+ * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
+ * Rafael Zalamena
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA.
+ */
+
+#ifndef _FRR_BFDD_NB_H_
+#define _FRR_BFDD_NB_H_
+
+extern const struct frr_yang_module_info frr_bfdd_info;
+
+/* Mandatory callbacks. */
+int bfdd_bfd_create(enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int bfdd_bfd_destroy(enum nb_event event, const struct lyd_node *dnode);
+int bfdd_bfd_sessions_single_hop_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int bfdd_bfd_sessions_single_hop_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+const void *bfdd_bfd_sessions_single_hop_get_next(const void *parent_list_entry,
+ const void *list_entry);
+int bfdd_bfd_sessions_single_hop_get_keys(const void *list_entry,
+ struct yang_list_keys *keys);
+const void *
+bfdd_bfd_sessions_single_hop_lookup_entry(const void *parent_list_entry,
+ const struct yang_list_keys *keys);
+int bfdd_bfd_sessions_single_hop_source_addr_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int bfdd_bfd_sessions_single_hop_source_addr_destroy(
+ enum nb_event event, const struct lyd_node *dnode);
+int bfdd_bfd_sessions_single_hop_detection_multiplier_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int bfdd_bfd_sessions_single_hop_required_receive_interval_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int bfdd_bfd_sessions_single_hop_administrative_down_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int bfdd_bfd_sessions_single_hop_echo_mode_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_local_state_get_elem(const char *xpath,
+ const void *list_entry);
+struct yang_data *bfdd_bfd_sessions_single_hop_stats_local_diagnostic_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *bfdd_bfd_sessions_single_hop_stats_local_multiplier_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_remote_discriminator_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *bfdd_bfd_sessions_single_hop_stats_remote_state_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *bfdd_bfd_sessions_single_hop_stats_remote_diagnostic_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *bfdd_bfd_sessions_single_hop_stats_remote_multiplier_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_negotiated_transmission_interval_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_negotiated_receive_interval_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *bfdd_bfd_sessions_single_hop_stats_detection_mode_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *bfdd_bfd_sessions_single_hop_stats_last_down_time_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *bfdd_bfd_sessions_single_hop_stats_last_up_time_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_session_down_count_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *bfdd_bfd_sessions_single_hop_stats_session_up_count_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_control_packet_input_count_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_control_packet_output_count_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_negotiated_echo_transmission_interval_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_echo_packet_input_count_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem(
+ const char *xpath, const void *list_entry);
+int bfdd_bfd_sessions_multi_hop_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int bfdd_bfd_sessions_multi_hop_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+const void *bfdd_bfd_sessions_multi_hop_get_next(const void *parent_list_entry,
+ const void *list_entry);
+int bfdd_bfd_sessions_multi_hop_get_keys(const void *list_entry,
+ struct yang_list_keys *keys);
+const void *
+bfdd_bfd_sessions_multi_hop_lookup_entry(const void *parent_list_entry,
+ const struct yang_list_keys *keys);
+int bfdd_bfd_sessions_multi_hop_detection_multiplier_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int bfdd_bfd_sessions_multi_hop_desired_transmission_interval_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int bfdd_bfd_sessions_multi_hop_required_receive_interval_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int bfdd_bfd_sessions_multi_hop_administrative_down_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+struct yang_data *
+bfdd_bfd_sessions_multi_hop_stats_local_discriminator_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_multi_hop_stats_local_state_get_elem(const char *xpath,
+ const void *list_entry);
+struct yang_data *bfdd_bfd_sessions_multi_hop_stats_local_diagnostic_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *bfdd_bfd_sessions_multi_hop_stats_local_multiplier_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_multi_hop_stats_remote_discriminator_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_multi_hop_stats_remote_state_get_elem(const char *xpath,
+ const void *list_entry);
+struct yang_data *bfdd_bfd_sessions_multi_hop_stats_remote_diagnostic_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *bfdd_bfd_sessions_multi_hop_stats_remote_multiplier_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_multi_hop_stats_negotiated_transmission_interval_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_multi_hop_stats_negotiated_receive_interval_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *bfdd_bfd_sessions_multi_hop_stats_detection_mode_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *bfdd_bfd_sessions_multi_hop_stats_last_down_time_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_multi_hop_stats_last_up_time_get_elem(const char *xpath,
+ const void *list_entry);
+struct yang_data *bfdd_bfd_sessions_multi_hop_stats_session_down_count_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *bfdd_bfd_sessions_multi_hop_stats_session_up_count_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_multi_hop_stats_control_packet_input_count_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_multi_hop_stats_control_packet_output_count_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_multi_hop_stats_negotiated_echo_transmission_interval_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_multi_hop_stats_echo_packet_input_count_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+bfdd_bfd_sessions_multi_hop_stats_echo_packet_output_count_get_elem(
+ const char *xpath, const void *list_entry);
+
+/* Optional 'cli_show' callbacks. */
+void bfd_cli_show_header(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void bfd_cli_show_header_end(struct vty *vty, struct lyd_node *dnode);
+void bfd_cli_show_single_hop_peer(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void bfd_cli_show_multi_hop_peer(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void bfd_cli_show_peer_end(struct vty *vty, struct lyd_node *dnode);
+void bfd_cli_show_mult(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void bfd_cli_show_tx(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void bfd_cli_show_rx(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void bfd_cli_show_shutdown(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void bfd_cli_show_echo(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void bfd_cli_show_echo_interval(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+
+#endif /* _FRR_BFDD_NB_H_ */
diff --git a/bfdd/bfdd_nb_config.c b/bfdd/bfdd_nb_config.c
new file mode 100644
index 0000000000..50f953115c
--- /dev/null
+++ b/bfdd/bfdd_nb_config.c
@@ -0,0 +1,520 @@
+/*
+ * BFD daemon northbound implementation.
+ *
+ * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
+ * Rafael Zalamena
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA.
+ */
+
+#include <zebra.h>
+
+#include "lib/log.h"
+#include "lib/northbound.h"
+
+#include "bfd.h"
+#include "bfdd_nb.h"
+
+/*
+ * Helpers.
+ */
+static void bfd_session_get_key(bool mhop, const struct lyd_node *dnode,
+ struct bfd_key *bk)
+{
+ const char *ifname = NULL, *vrfname = NULL;
+ struct sockaddr_any psa, lsa;
+
+ /* Required destination parameter. */
+ strtosa(yang_dnode_get_string(dnode, "./dest-addr"), &psa);
+
+ /* Get optional source address. */
+ memset(&lsa, 0, sizeof(lsa));
+ if (yang_dnode_exists(dnode, "./source-addr"))
+ strtosa(yang_dnode_get_string(dnode, "./source-addr"), &lsa);
+
+ /* Get optional interface and vrf names. */
+ if (yang_dnode_exists(dnode, "./interface"))
+ ifname = yang_dnode_get_string(dnode, "./interface");
+ if (yang_dnode_exists(dnode, "./vrf"))
+ vrfname = yang_dnode_get_string(dnode, "./vrf");
+
+ /* Generate the corresponding key. */
+ gen_bfd_key(bk, &psa, &lsa, mhop, ifname, vrfname);
+}
+
+static int bfd_session_create(enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource, bool mhop)
+{
+ struct bfd_session *bs;
+ const char *ifname;
+ struct bfd_key bk;
+ struct prefix p;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ /*
+ * When `dest-addr` is IPv6 and link-local we must
+ * require interface name, otherwise we can't figure
+ * which interface to use to send the packets.
+ */
+ yang_dnode_get_prefix(&p, dnode, "./dest-addr");
+
+ /*
+ * To support old FRR versions we must allow empty
+ * interface to be specified, however that should
+ * change in the future.
+ */
+ if (yang_dnode_exists(dnode, "./interface"))
+ ifname = yang_dnode_get_string(dnode, "./interface");
+ else
+ ifname = "";
+
+ if (p.family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)
+ && strlen(ifname) == 0) {
+ zlog_warn(
+ "%s: when using link-local you must specify "
+ "an interface.",
+ __func__);
+ return NB_ERR_VALIDATION;
+ }
+ break;
+
+ case NB_EV_PREPARE:
+ bfd_session_get_key(mhop, dnode, &bk);
+ bs = bfd_key_lookup(bk);
+
+ /* This session was already configured by another daemon. */
+ if (bs != NULL) {
+ /* Now it is configured also by CLI. */
+ BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG);
+ bs->refcount++;
+
+ resource->ptr = bs;
+ break;
+ }
+
+ bs = bfd_session_new();
+ if (bs == NULL)
+ return NB_ERR_RESOURCE;
+
+ /* Fill the session key. */
+ bfd_session_get_key(mhop, dnode, &bs->key);
+
+ /* Set configuration flags. */
+ bs->refcount = 1;
+ BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG);
+ if (mhop)
+ BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_MH);
+ if (bs->key.family == AF_INET6)
+ BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_IPV6);
+
+ resource->ptr = bs;
+ break;
+
+ case NB_EV_APPLY:
+ bs = resource->ptr;
+
+ /* Only attempt to registrate if freshly allocated. */
+ if (bs->discrs.my_discr == 0 && bs_registrate(bs) == NULL)
+ return NB_ERR_RESOURCE;
+
+ nb_running_set_entry(dnode, bs);
+ break;
+
+ case NB_EV_ABORT:
+ bs = resource->ptr;
+ if (bs->refcount <= 1)
+ bfd_session_free(resource->ptr);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int bfd_session_destroy(enum nb_event event,
+ const struct lyd_node *dnode, bool mhop)
+{
+ struct bfd_session *bs;
+ struct bfd_key bk;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ bfd_session_get_key(mhop, dnode, &bk);
+ if (bfd_key_lookup(bk) == NULL)
+ return NB_ERR_INCONSISTENCY;
+ break;
+
+ case NB_EV_PREPARE:
+ /* NOTHING */
+ break;
+
+ case NB_EV_APPLY:
+ bs = nb_running_unset_entry(dnode);
+ /* CLI is not using this session anymore. */
+ if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) == 0)
+ break;
+
+ BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG);
+ bs->refcount--;
+ /* There are still daemons using it. */
+ if (bs->refcount > 0)
+ break;
+
+ bfd_session_free(bs);
+ break;
+
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd
+ */
+int bfdd_bfd_create(enum nb_event event,
+ const struct lyd_node *dnode __attribute__((__unused__)),
+ union nb_resource *resource __attribute__((__unused__)))
+{
+ /* NOTHING */
+ return NB_OK;
+}
+
+int bfdd_bfd_destroy(enum nb_event event, const struct lyd_node *dnode)
+{
+ switch (event) {
+ case NB_EV_VALIDATE:
+ /* NOTHING */
+ return NB_OK;
+
+ case NB_EV_PREPARE:
+ /* NOTHING */
+ return NB_OK;
+
+ case NB_EV_APPLY:
+ bfd_sessions_remove_manual();
+ break;
+
+ case NB_EV_ABORT:
+ /* NOTHING */
+ return NB_OK;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop
+ */
+int bfdd_bfd_sessions_single_hop_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return bfd_session_create(event, dnode, resource, false);
+}
+
+int bfdd_bfd_sessions_single_hop_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ return bfd_session_destroy(event, dnode, false);
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/source-addr
+ */
+int bfdd_bfd_sessions_single_hop_source_addr_modify(enum nb_event event
+ __attribute__((__unused__)),
+ const struct lyd_node *dnode
+ __attribute__((__unused__)),
+ union nb_resource *resource
+ __attribute__((__unused__)))
+{
+ return NB_OK;
+}
+
+int bfdd_bfd_sessions_single_hop_source_addr_destroy(
+ enum nb_event event __attribute__((__unused__)),
+ const struct lyd_node *dnode __attribute__((__unused__)))
+{
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/detection-multiplier
+ */
+int bfdd_bfd_sessions_single_hop_detection_multiplier_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource __attribute__((__unused__)))
+{
+ uint8_t detection_multiplier = yang_dnode_get_uint8(dnode, NULL);
+ struct bfd_session *bs;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ break;
+
+ case NB_EV_PREPARE:
+ /* NOTHING */
+ break;
+
+ case NB_EV_APPLY:
+ bs = nb_running_get_entry(dnode, NULL, true);
+ bs->detect_mult = detection_multiplier;
+ break;
+
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/desired-transmission-interval
+ */
+int bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource __attribute__((__unused__)))
+{
+ uint32_t tx_interval = yang_dnode_get_uint32(dnode, NULL);
+ struct bfd_session *bs;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ if (tx_interval < 10000 || tx_interval > 60000000)
+ return NB_ERR_VALIDATION;
+ break;
+
+ case NB_EV_PREPARE:
+ /* NOTHING */
+ break;
+
+ case NB_EV_APPLY:
+ bs = nb_running_get_entry(dnode, NULL, true);
+ if (tx_interval == bs->timers.desired_min_tx)
+ return NB_OK;
+
+ bs->timers.desired_min_tx = tx_interval;
+ bfd_set_polling(bs);
+ break;
+
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/required-receive-interval
+ */
+int bfdd_bfd_sessions_single_hop_required_receive_interval_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource __attribute__((__unused__)))
+{
+ uint32_t rx_interval = yang_dnode_get_uint32(dnode, NULL);
+ struct bfd_session *bs;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ if (rx_interval < 10000 || rx_interval > 60000000)
+ return NB_ERR_VALIDATION;
+ break;
+
+ case NB_EV_PREPARE:
+ /* NOTHING */
+ break;
+
+ case NB_EV_APPLY:
+ bs = nb_running_get_entry(dnode, NULL, true);
+ if (rx_interval == bs->timers.required_min_rx)
+ return NB_OK;
+
+ bs->timers.required_min_rx = rx_interval;
+ bfd_set_polling(bs);
+ break;
+
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/administrative-down
+ */
+int bfdd_bfd_sessions_single_hop_administrative_down_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource __attribute__((__unused__)))
+{
+ bool shutdown = yang_dnode_get_bool(dnode, NULL);
+ struct bfd_session *bs;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ return NB_OK;
+
+ case NB_EV_APPLY:
+ break;
+
+ case NB_EV_ABORT:
+ return NB_OK;
+ }
+
+ bs = nb_running_get_entry(dnode, NULL, true);
+
+ if (shutdown == false) {
+ if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
+ return NB_OK;
+
+ BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
+
+ /* Change and notify state change. */
+ bs->ses_state = PTM_BFD_DOWN;
+ control_notify(bs, bs->ses_state);
+
+ /* Enable all timers. */
+ bfd_recvtimer_update(bs);
+ bfd_xmttimer_update(bs, bs->xmt_TO);
+ if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) {
+ bfd_echo_recvtimer_update(bs);
+ bfd_echo_xmttimer_update(bs, bs->echo_xmt_TO);
+ }
+ } else {
+ if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
+ return NB_OK;
+
+ BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
+
+ /* Disable all events. */
+ bfd_recvtimer_delete(bs);
+ bfd_echo_recvtimer_delete(bs);
+ bfd_xmttimer_delete(bs);
+ bfd_echo_xmttimer_delete(bs);
+
+ /* Change and notify state change. */
+ bs->ses_state = PTM_BFD_ADM_DOWN;
+ control_notify(bs, bs->ses_state);
+
+ ptm_bfd_snd(bs, 0);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/echo-mode
+ */
+int bfdd_bfd_sessions_single_hop_echo_mode_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource
+ __attribute__((__unused__)))
+{
+ bool echo = yang_dnode_get_bool(dnode, NULL);
+ struct bfd_session *bs;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ return NB_OK;
+
+ case NB_EV_APPLY:
+ break;
+
+ case NB_EV_ABORT:
+ return NB_OK;
+ }
+
+ bs = nb_running_get_entry(dnode, NULL, true);
+
+ if (echo == false) {
+ if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
+ return NB_OK;
+
+ BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
+ ptm_bfd_echo_stop(bs);
+ } else {
+ if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
+ return NB_OK;
+
+ BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
+ /* Apply setting immediately. */
+ if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
+ bs_echo_timer_handler(bs);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-bfdd:bfdd/bfd/sessions/single-hop/desired-echo-transmission-interval
+ */
+int bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource __attribute__((__unused__)))
+{
+ uint32_t echo_interval = yang_dnode_get_uint32(dnode, NULL);
+ struct bfd_session *bs;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ if (echo_interval < 10000 || echo_interval > 60000000)
+ return NB_ERR_VALIDATION;
+ break;
+
+ case NB_EV_PREPARE:
+ /* NOTHING */
+ break;
+
+ case NB_EV_APPLY:
+ bs = nb_running_get_entry(dnode, NULL, true);
+ if (echo_interval == bs->timers.required_min_echo)
+ return NB_OK;
+
+ bs->timers.required_min_echo = echo_interval;
+ break;
+
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/multi-hop
+ */
+int bfdd_bfd_sessions_multi_hop_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return bfd_session_create(event, dnode, resource, true);
+}
+
+int bfdd_bfd_sessions_multi_hop_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ return bfd_session_destroy(event, dnode, true);
+}
diff --git a/bfdd/bfdd_nb_state.c b/bfdd/bfdd_nb_state.c
new file mode 100644
index 0000000000..dfca3d1417
--- /dev/null
+++ b/bfdd/bfdd_nb_state.c
@@ -0,0 +1,383 @@
+/*
+ * BFD daemon northbound implementation.
+ *
+ * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
+ * Rafael Zalamena
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA.
+ */
+
+#include <zebra.h>
+
+#include "lib/log.h"
+#include "lib/northbound.h"
+
+#include "bfd.h"
+#include "bfdd_nb.h"
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop
+ */
+const void *bfdd_bfd_sessions_single_hop_get_next(const void *parent_list_entry
+ __attribute__((__unused__)),
+ const void *list_entry)
+{
+ return bfd_session_next(list_entry, false);
+}
+
+int bfdd_bfd_sessions_single_hop_get_keys(const void *list_entry,
+ struct yang_list_keys *keys)
+{
+ const struct bfd_session *bs = list_entry;
+ char dstbuf[INET6_ADDRSTRLEN];
+
+ inet_ntop(bs->key.family, &bs->key.peer, dstbuf, sizeof(dstbuf));
+
+ keys->num = 3;
+ strlcpy(keys->key[0], dstbuf, sizeof(keys->key[0]));
+ strlcpy(keys->key[1], bs->key.ifname, sizeof(keys->key[1]));
+ strlcpy(keys->key[2], bs->key.vrfname, sizeof(keys->key[2]));
+
+ return NB_OK;
+}
+
+const void *
+bfdd_bfd_sessions_single_hop_lookup_entry(const void *parent_list_entry
+ __attribute__((__unused__)),
+ const struct yang_list_keys *keys)
+{
+ const char *dest_addr = keys->key[0];
+ const char *ifname = keys->key[1];
+ const char *vrf = keys->key[2];
+ struct sockaddr_any psa, lsa;
+ struct bfd_key bk;
+
+ strtosa(dest_addr, &psa);
+ memset(&lsa, 0, sizeof(lsa));
+ gen_bfd_key(&bk, &psa, &lsa, false, ifname, vrf);
+
+ return bfd_key_lookup(bk);
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-discriminator
+ */
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct bfd_session *bs = list_entry;
+
+ return yang_data_new_uint32(xpath, bs->discrs.my_discr);
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-state
+ */
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_local_state_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct bfd_session *bs = list_entry;
+
+ return yang_data_new_enum(xpath, bs->ses_state);
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-diagnostic
+ */
+struct yang_data *bfdd_bfd_sessions_single_hop_stats_local_diagnostic_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct bfd_session *bs = list_entry;
+
+ return yang_data_new_enum(xpath, bs->local_diag);
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-multiplier
+ */
+struct yang_data *bfdd_bfd_sessions_single_hop_stats_local_multiplier_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct bfd_session *bs = list_entry;
+
+ return yang_data_new_int8(xpath, bs->detect_mult);
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-discriminator
+ */
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_remote_discriminator_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct bfd_session *bs = list_entry;
+
+ if (bs->discrs.remote_discr == 0)
+ return NULL;
+
+ return yang_data_new_uint32(xpath, bs->discrs.remote_discr);
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-state
+ */
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_remote_state_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct bfd_session *bs = list_entry;
+
+ return yang_data_new_enum(xpath, bs->ses_state);
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-diagnostic
+ */
+struct yang_data *bfdd_bfd_sessions_single_hop_stats_remote_diagnostic_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct bfd_session *bs = list_entry;
+
+ return yang_data_new_enum(xpath, bs->remote_diag);
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-multiplier
+ */
+struct yang_data *bfdd_bfd_sessions_single_hop_stats_remote_multiplier_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct bfd_session *bs = list_entry;
+
+ return yang_data_new_int8(xpath, bs->remote_detect_mult);
+}
+
+/*
+ * XPath:
+ * /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-transmission-interval
+ */
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_negotiated_transmission_interval_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct bfd_session *bs = list_entry;
+
+ return yang_data_new_uint32(xpath, bs->remote_timers.desired_min_tx);
+}
+
+/*
+ * XPath:
+ * /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-receive-interval
+ */
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_negotiated_receive_interval_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct bfd_session *bs = list_entry;
+
+ return yang_data_new_uint32(xpath, bs->remote_timers.required_min_rx);
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/detection-mode
+ */
+struct yang_data *bfdd_bfd_sessions_single_hop_stats_detection_mode_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct bfd_session *bs = list_entry;
+ int detection_mode;
+
+ /*
+ * Detection mode:
+ * 1. Async with echo
+ * 2. Async without echo
+ * 3. Demand with echo
+ * 4. Demand without echo
+ *
+ * TODO: support demand mode.
+ */
+ if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
+ detection_mode = 1;
+ else
+ detection_mode = 2;
+
+ return yang_data_new_enum(xpath, detection_mode);
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/last-down-time
+ */
+struct yang_data *bfdd_bfd_sessions_single_hop_stats_last_down_time_get_elem(
+ const char *xpath __attribute__((__unused__)),
+ const void *list_entry __attribute__((__unused__)))
+{
+ /*
+ * TODO: implement me.
+ *
+ * No yang support for time elements yet.
+ */
+ return NULL;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/last-up-time
+ */
+struct yang_data *bfdd_bfd_sessions_single_hop_stats_last_up_time_get_elem(
+ const char *xpath __attribute__((__unused__)),
+ const void *list_entry __attribute__((__unused__)))
+{
+ /*
+ * TODO: implement me.
+ *
+ * No yang support for time elements yet.
+ */
+ return NULL;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/session-down-count
+ */
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_session_down_count_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct bfd_session *bs = list_entry;
+
+ return yang_data_new_uint64(xpath, bs->stats.session_down);
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/session-up-count
+ */
+struct yang_data *bfdd_bfd_sessions_single_hop_stats_session_up_count_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct bfd_session *bs = list_entry;
+
+ return yang_data_new_uint64(xpath, bs->stats.session_up);
+}
+
+/*
+ * XPath:
+ * /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/control-packet-input-count
+ */
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_control_packet_input_count_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct bfd_session *bs = list_entry;
+
+ return yang_data_new_uint64(xpath, bs->stats.rx_ctrl_pkt);
+}
+
+/*
+ * XPath:
+ * /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/control-packet-output-count
+ */
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_control_packet_output_count_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct bfd_session *bs = list_entry;
+
+ return yang_data_new_uint64(xpath, bs->stats.tx_ctrl_pkt);
+}
+
+/*
+ * XPath:
+ * /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-echo-transmission-interval
+ */
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_negotiated_echo_transmission_interval_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct bfd_session *bs = list_entry;
+
+ return yang_data_new_uint32(xpath, bs->remote_timers.required_min_echo);
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/echo-packet-input-count
+ */
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_echo_packet_input_count_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct bfd_session *bs = list_entry;
+
+ return yang_data_new_uint64(xpath, bs->stats.rx_echo_pkt);
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/echo-packet-output-count
+ */
+struct yang_data *
+bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct bfd_session *bs = list_entry;
+
+ return yang_data_new_uint64(xpath, bs->stats.tx_echo_pkt);
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/multi-hop
+ */
+const void *bfdd_bfd_sessions_multi_hop_get_next(const void *parent_list_entry
+ __attribute__((__unused__)),
+ const void *list_entry)
+{
+ return bfd_session_next(list_entry, true);
+}
+
+int bfdd_bfd_sessions_multi_hop_get_keys(const void *list_entry,
+ struct yang_list_keys *keys)
+{
+ const struct bfd_session *bs = list_entry;
+ char dstbuf[INET6_ADDRSTRLEN], srcbuf[INET6_ADDRSTRLEN];
+
+ inet_ntop(bs->key.family, &bs->key.peer, dstbuf, sizeof(dstbuf));
+ inet_ntop(bs->key.family, &bs->key.local, srcbuf, sizeof(srcbuf));
+
+ keys->num = 4;
+ strlcpy(keys->key[0], srcbuf, sizeof(keys->key[0]));
+ strlcpy(keys->key[1], dstbuf, sizeof(keys->key[1]));
+ strlcpy(keys->key[2], bs->key.ifname, sizeof(keys->key[2]));
+ strlcpy(keys->key[3], bs->key.vrfname, sizeof(keys->key[3]));
+
+ return NB_OK;
+}
+
+const void *
+bfdd_bfd_sessions_multi_hop_lookup_entry(const void *parent_list_entry
+ __attribute__((__unused__)),
+ const struct yang_list_keys *keys)
+{
+ const char *source_addr = keys->key[0];
+ const char *dest_addr = keys->key[1];
+ const char *ifname = keys->key[2];
+ const char *vrf = keys->key[3];
+ struct sockaddr_any psa, lsa;
+ struct bfd_key bk;
+
+ strtosa(dest_addr, &psa);
+ strtosa(source_addr, &lsa);
+ gen_bfd_key(&bk, &psa, &lsa, true, ifname, vrf);
+
+ return bfd_key_lookup(bk);
+}
diff --git a/bfdd/bfdd_northbound.c b/bfdd/bfdd_northbound.c
deleted file mode 100644
index 975fc7b31f..0000000000
--- a/bfdd/bfdd_northbound.c
+++ /dev/null
@@ -1,1236 +0,0 @@
-/*
- * BFD daemon northbound implementation.
- *
- * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
- * Rafael Zalamena
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301 USA.
- */
-
-#include <zebra.h>
-
-#include "lib/log.h"
-#include "lib/northbound.h"
-
-#include "bfd.h"
-
-/*
- * Helpers.
- */
-static void bfd_session_get_key(bool mhop, const struct lyd_node *dnode,
- struct bfd_key *bk)
-{
- const char *ifname = NULL, *vrfname = NULL;
- struct sockaddr_any psa, lsa;
-
- /* Required destination parameter. */
- strtosa(yang_dnode_get_string(dnode, "./dest-addr"), &psa);
-
- /* Get optional source address. */
- memset(&lsa, 0, sizeof(lsa));
- if (yang_dnode_exists(dnode, "./source-addr"))
- strtosa(yang_dnode_get_string(dnode, "./source-addr"), &lsa);
-
- /* Get optional interface and vrf names. */
- if (yang_dnode_exists(dnode, "./interface"))
- ifname = yang_dnode_get_string(dnode, "./interface");
- if (yang_dnode_exists(dnode, "./vrf"))
- vrfname = yang_dnode_get_string(dnode, "./vrf");
-
- /* Generate the corresponding key. */
- gen_bfd_key(bk, &psa, &lsa, mhop, ifname, vrfname);
-}
-
-static int bfd_session_create(enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource, bool mhop)
-{
- struct bfd_session *bs;
- const char *ifname;
- struct bfd_key bk;
- struct prefix p;
-
- switch (event) {
- case NB_EV_VALIDATE:
- /*
- * When `dest-addr` is IPv6 and link-local we must
- * require interface name, otherwise we can't figure
- * which interface to use to send the packets.
- */
- yang_dnode_get_prefix(&p, dnode, "./dest-addr");
-
- /*
- * To support old FRR versions we must allow empty
- * interface to be specified, however that should
- * change in the future.
- */
- if (yang_dnode_exists(dnode, "./interface"))
- ifname = yang_dnode_get_string(dnode, "./interface");
- else
- ifname = "";
-
- if (p.family == AF_INET6
- && IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)
- && strlen(ifname) == 0) {
- zlog_warn("%s: when using link-local you must specify "
- "an interface.", __func__);
- return NB_ERR_VALIDATION;
- }
- break;
-
- case NB_EV_PREPARE:
- bfd_session_get_key(mhop, dnode, &bk);
- bs = bfd_key_lookup(bk);
-
- /* This session was already configured by another daemon. */
- if (bs != NULL) {
- /* Now it is configured also by CLI. */
- BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG);
- bs->refcount++;
-
- resource->ptr = bs;
- break;
- }
-
- bs = bfd_session_new();
- if (bs == NULL)
- return NB_ERR_RESOURCE;
-
- /* Fill the session key. */
- bfd_session_get_key(mhop, dnode, &bs->key);
-
- /* Set configuration flags. */
- bs->refcount = 1;
- BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG);
- if (mhop)
- BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_MH);
- if (bs->key.family == AF_INET6)
- BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_IPV6);
-
- resource->ptr = bs;
- break;
-
- case NB_EV_APPLY:
- bs = resource->ptr;
-
- /* Only attempt to registrate if freshly allocated. */
- if (bs->discrs.my_discr == 0 && bs_registrate(bs) == NULL)
- return NB_ERR_RESOURCE;
-
- nb_running_set_entry(dnode, bs);
- break;
-
- case NB_EV_ABORT:
- bs = resource->ptr;
- if (bs->refcount <= 1)
- bfd_session_free(resource->ptr);
- break;
- }
-
- return NB_OK;
-}
-
-static int bfd_session_destroy(enum nb_event event,
- const struct lyd_node *dnode, bool mhop)
-{
- struct bfd_session *bs;
- struct bfd_key bk;
-
- switch (event) {
- case NB_EV_VALIDATE:
- bfd_session_get_key(mhop, dnode, &bk);
- if (bfd_key_lookup(bk) == NULL)
- return NB_ERR_INCONSISTENCY;
- break;
-
- case NB_EV_PREPARE:
- /* NOTHING */
- break;
-
- case NB_EV_APPLY:
- bs = nb_running_unset_entry(dnode);
- /* CLI is not using this session anymore. */
- if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) == 0)
- break;
-
- BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG);
- bs->refcount--;
- /* There are still daemons using it. */
- if (bs->refcount > 0)
- break;
-
- bfd_session_free(bs);
- break;
-
- case NB_EV_ABORT:
- /* NOTHING */
- break;
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd
- */
-static int bfdd_bfd_create(enum nb_event event,
- const struct lyd_node *dnode
- __attribute__((__unused__)),
- union nb_resource *resource
- __attribute__((__unused__)))
-{
- /* NOTHING */
- return NB_OK;
-}
-
-static int bfdd_bfd_destroy(enum nb_event event, const struct lyd_node *dnode)
-{
- switch (event) {
- case NB_EV_VALIDATE:
- /* NOTHING */
- return NB_OK;
-
- case NB_EV_PREPARE:
- /* NOTHING */
- return NB_OK;
-
- case NB_EV_APPLY:
- bfd_sessions_remove_manual();
- break;
-
- case NB_EV_ABORT:
- /* NOTHING */
- return NB_OK;
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop
- */
-static int bfdd_bfd_sessions_single_hop_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return bfd_session_create(event, dnode, resource, false);
-}
-
-static int bfdd_bfd_sessions_single_hop_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- return bfd_session_destroy(event, dnode, false);
-}
-
-static const void *
-bfdd_bfd_sessions_single_hop_get_next(const void *parent_list_entry
- __attribute__((__unused__)),
- const void *list_entry)
-{
- return bfd_session_next(list_entry, false);
-}
-
-static int bfdd_bfd_sessions_single_hop_get_keys(const void *list_entry,
- struct yang_list_keys *keys)
-{
- const struct bfd_session *bs = list_entry;
- char dstbuf[INET6_ADDRSTRLEN];
-
- inet_ntop(bs->key.family, &bs->key.peer, dstbuf, sizeof(dstbuf));
-
- keys->num = 3;
- strlcpy(keys->key[0], dstbuf, sizeof(keys->key[0]));
- strlcpy(keys->key[1], bs->key.ifname, sizeof(keys->key[1]));
- strlcpy(keys->key[2], bs->key.vrfname, sizeof(keys->key[2]));
-
- return NB_OK;
-}
-
-static const void *
-bfdd_bfd_sessions_single_hop_lookup_entry(const void *parent_list_entry
- __attribute__((__unused__)),
- const struct yang_list_keys *keys)
-{
- const char *dest_addr = keys->key[0];
- const char *ifname = keys->key[1];
- const char *vrf = keys->key[2];
- struct sockaddr_any psa, lsa;
- struct bfd_key bk;
-
- strtosa(dest_addr, &psa);
- memset(&lsa, 0, sizeof(lsa));
- gen_bfd_key(&bk, &psa, &lsa, false, ifname, vrf);
-
- return bfd_key_lookup(bk);
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/source-addr
- */
-static int bfdd_bfd_sessions_single_hop_source_addr_modify(
- enum nb_event event __attribute__((__unused__)),
- const struct lyd_node *dnode __attribute__((__unused__)),
- union nb_resource *resource __attribute__((__unused__)))
-{
- return NB_OK;
-}
-
-static int bfdd_bfd_sessions_single_hop_source_addr_destroy(
- enum nb_event event __attribute__((__unused__)),
- const struct lyd_node *dnode __attribute__((__unused__)))
-{
- return NB_OK;
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/detection-multiplier
- */
-static int bfdd_bfd_sessions_single_hop_detection_multiplier_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource __attribute__((__unused__)))
-{
- uint8_t detection_multiplier = yang_dnode_get_uint8(dnode, NULL);
- struct bfd_session *bs;
-
- switch (event) {
- case NB_EV_VALIDATE:
- break;
-
- case NB_EV_PREPARE:
- /* NOTHING */
- break;
-
- case NB_EV_APPLY:
- bs = nb_running_get_entry(dnode, NULL, true);
- bs->detect_mult = detection_multiplier;
- break;
-
- case NB_EV_ABORT:
- /* NOTHING */
- break;
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/desired-transmission-interval
- */
-static int bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource __attribute__((__unused__)))
-{
- uint32_t tx_interval = yang_dnode_get_uint32(dnode, NULL);
- struct bfd_session *bs;
-
- switch (event) {
- case NB_EV_VALIDATE:
- if (tx_interval < 10000 || tx_interval > 60000000)
- return NB_ERR_VALIDATION;
- break;
-
- case NB_EV_PREPARE:
- /* NOTHING */
- break;
-
- case NB_EV_APPLY:
- bs = nb_running_get_entry(dnode, NULL, true);
- if (tx_interval == bs->timers.desired_min_tx)
- return NB_OK;
-
- bs->timers.desired_min_tx = tx_interval;
- bfd_set_polling(bs);
- break;
-
- case NB_EV_ABORT:
- /* NOTHING */
- break;
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/required-receive-interval
- */
-static int bfdd_bfd_sessions_single_hop_required_receive_interval_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource __attribute__((__unused__)))
-{
- uint32_t rx_interval = yang_dnode_get_uint32(dnode, NULL);
- struct bfd_session *bs;
-
- switch (event) {
- case NB_EV_VALIDATE:
- if (rx_interval < 10000 || rx_interval > 60000000)
- return NB_ERR_VALIDATION;
- break;
-
- case NB_EV_PREPARE:
- /* NOTHING */
- break;
-
- case NB_EV_APPLY:
- bs = nb_running_get_entry(dnode, NULL, true);
- if (rx_interval == bs->timers.required_min_rx)
- return NB_OK;
-
- bs->timers.required_min_rx = rx_interval;
- bfd_set_polling(bs);
- break;
-
- case NB_EV_ABORT:
- /* NOTHING */
- break;
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/administrative-down
- */
-static int bfdd_bfd_sessions_single_hop_administrative_down_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource __attribute__((__unused__)))
-{
- bool shutdown = yang_dnode_get_bool(dnode, NULL);
- struct bfd_session *bs;
-
- switch (event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- return NB_OK;
-
- case NB_EV_APPLY:
- break;
-
- case NB_EV_ABORT:
- return NB_OK;
- }
-
- bs = nb_running_get_entry(dnode, NULL, true);
-
- if (shutdown == false) {
- if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
- return NB_OK;
-
- BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
-
- /* Change and notify state change. */
- bs->ses_state = PTM_BFD_DOWN;
- control_notify(bs);
-
- /* Enable all timers. */
- bfd_recvtimer_update(bs);
- bfd_xmttimer_update(bs, bs->xmt_TO);
- if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) {
- bfd_echo_recvtimer_update(bs);
- bfd_echo_xmttimer_update(bs, bs->echo_xmt_TO);
- }
- } else {
- if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
- return NB_OK;
-
- BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
-
- /* Disable all events. */
- bfd_recvtimer_delete(bs);
- bfd_echo_recvtimer_delete(bs);
- bfd_xmttimer_delete(bs);
- bfd_echo_xmttimer_delete(bs);
-
- /* Change and notify state change. */
- bs->ses_state = PTM_BFD_ADM_DOWN;
- control_notify(bs);
-
- ptm_bfd_snd(bs, 0);
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/echo-mode
- */
-static int bfdd_bfd_sessions_single_hop_echo_mode_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource __attribute__((__unused__)))
-{
- bool echo = yang_dnode_get_bool(dnode, NULL);
- struct bfd_session *bs;
-
- switch (event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- return NB_OK;
-
- case NB_EV_APPLY:
- break;
-
- case NB_EV_ABORT:
- return NB_OK;
- }
-
- bs = nb_running_get_entry(dnode, NULL, true);
-
- if (echo == false) {
- if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
- return NB_OK;
-
- BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
- ptm_bfd_echo_stop(bs);
- } else {
- if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
- return NB_OK;
-
- BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
- /* Apply setting immediately. */
- if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
- bs_echo_timer_handler(bs);
- }
-
- return NB_OK;
-}
-
-/*
- * XPath:
- * /frr-bfdd:bfdd/bfd/sessions/single-hop/desired-echo-transmission-interval
- */
-static int
-bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource __attribute__((__unused__)))
-{
- uint32_t echo_interval = yang_dnode_get_uint32(dnode, NULL);
- struct bfd_session *bs;
-
- switch (event) {
- case NB_EV_VALIDATE:
- if (echo_interval < 10000 || echo_interval > 60000000)
- return NB_ERR_VALIDATION;
- break;
-
- case NB_EV_PREPARE:
- /* NOTHING */
- break;
-
- case NB_EV_APPLY:
- bs = nb_running_get_entry(dnode, NULL, true);
- if (echo_interval == bs->timers.required_min_echo)
- return NB_OK;
-
- bs->timers.required_min_echo = echo_interval;
- break;
-
- case NB_EV_ABORT:
- /* NOTHING */
- break;
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-discriminator
- */
-static struct yang_data *
-bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct bfd_session *bs = list_entry;
-
- return yang_data_new_uint32(xpath, bs->discrs.my_discr);
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-state
- */
-static struct yang_data *
-bfdd_bfd_sessions_single_hop_stats_local_state_get_elem(const char *xpath,
- const void *list_entry)
-{
- const struct bfd_session *bs = list_entry;
-
- return yang_data_new_enum(xpath, bs->ses_state);
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-diagnostic
- */
-static struct yang_data *
-bfdd_bfd_sessions_single_hop_stats_local_diagnostic_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct bfd_session *bs = list_entry;
-
- return yang_data_new_enum(xpath, bs->local_diag);
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-multiplier
- */
-static struct yang_data *
-bfdd_bfd_sessions_single_hop_stats_local_multiplier_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct bfd_session *bs = list_entry;
-
- return yang_data_new_int8(xpath, bs->detect_mult);
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-discriminator
- */
-static struct yang_data *
-bfdd_bfd_sessions_single_hop_stats_remote_discriminator_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct bfd_session *bs = list_entry;
-
- if (bs->discrs.remote_discr == 0)
- return NULL;
-
- return yang_data_new_uint32(xpath, bs->discrs.remote_discr);
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-state
- */
-static struct yang_data *
-bfdd_bfd_sessions_single_hop_stats_remote_state_get_elem(const char *xpath,
- const void *list_entry)
-{
- const struct bfd_session *bs = list_entry;
-
- return yang_data_new_enum(xpath, bs->ses_state);
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-diagnostic
- */
-static struct yang_data *
-bfdd_bfd_sessions_single_hop_stats_remote_diagnostic_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct bfd_session *bs = list_entry;
-
- return yang_data_new_enum(xpath, bs->remote_diag);
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-multiplier
- */
-static struct yang_data *
-bfdd_bfd_sessions_single_hop_stats_remote_multiplier_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct bfd_session *bs = list_entry;
-
- return yang_data_new_int8(xpath, bs->remote_detect_mult);
-}
-
-/*
- * XPath:
- * /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-transmission-interval
- */
-static struct yang_data *
-bfdd_bfd_sessions_single_hop_stats_negotiated_transmission_interval_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct bfd_session *bs = list_entry;
-
- return yang_data_new_uint32(xpath, bs->remote_timers.desired_min_tx);
-}
-
-/*
- * XPath:
- * /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-receive-interval
- */
-static struct yang_data *
-bfdd_bfd_sessions_single_hop_stats_negotiated_receive_interval_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct bfd_session *bs = list_entry;
-
- return yang_data_new_uint32(xpath, bs->remote_timers.required_min_rx);
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/detection-mode
- */
-static struct yang_data *
-bfdd_bfd_sessions_single_hop_stats_detection_mode_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct bfd_session *bs = list_entry;
- int detection_mode;
-
- /*
- * Detection mode:
- * 1. Async with echo
- * 2. Async without echo
- * 3. Demand with echo
- * 4. Demand without echo
- *
- * TODO: support demand mode.
- */
- if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
- detection_mode = 1;
- else
- detection_mode = 2;
-
- return yang_data_new_enum(xpath, detection_mode);
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/last-down-time
- */
-static struct yang_data *
-bfdd_bfd_sessions_single_hop_stats_last_down_time_get_elem(
- const char *xpath __attribute__((__unused__)),
- const void *list_entry __attribute__((__unused__)))
-{
- /*
- * TODO: implement me.
- *
- * No yang support for time elements yet.
- */
- return NULL;
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/last-up-time
- */
-static struct yang_data *
-bfdd_bfd_sessions_single_hop_stats_last_up_time_get_elem(
- const char *xpath __attribute__((__unused__)),
- const void *list_entry __attribute__((__unused__)))
-{
- /*
- * TODO: implement me.
- *
- * No yang support for time elements yet.
- */
- return NULL;
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/session-down-count
- */
-static struct yang_data *
-bfdd_bfd_sessions_single_hop_stats_session_down_count_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct bfd_session *bs = list_entry;
-
- return yang_data_new_uint64(xpath, bs->stats.session_down);
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/session-up-count
- */
-static struct yang_data *
-bfdd_bfd_sessions_single_hop_stats_session_up_count_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct bfd_session *bs = list_entry;
-
- return yang_data_new_uint64(xpath, bs->stats.session_up);
-}
-
-/*
- * XPath:
- * /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/control-packet-input-count
- */
-static struct yang_data *
-bfdd_bfd_sessions_single_hop_stats_control_packet_input_count_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct bfd_session *bs = list_entry;
-
- return yang_data_new_uint64(xpath, bs->stats.rx_ctrl_pkt);
-}
-
-/*
- * XPath:
- * /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/control-packet-output-count
- */
-static struct yang_data *
-bfdd_bfd_sessions_single_hop_stats_control_packet_output_count_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct bfd_session *bs = list_entry;
-
- return yang_data_new_uint64(xpath, bs->stats.tx_ctrl_pkt);
-}
-
-/*
- * XPath:
- * /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-echo-transmission-interval
- */
-static struct yang_data *
-bfdd_bfd_sessions_single_hop_stats_negotiated_echo_transmission_interval_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct bfd_session *bs = list_entry;
-
- return yang_data_new_uint32(xpath, bs->remote_timers.required_min_echo);
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/echo-packet-input-count
- */
-static struct yang_data *
-bfdd_bfd_sessions_single_hop_stats_echo_packet_input_count_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct bfd_session *bs = list_entry;
-
- return yang_data_new_uint64(xpath, bs->stats.rx_echo_pkt);
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/echo-packet-output-count
- */
-static struct yang_data *
-bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct bfd_session *bs = list_entry;
-
- return yang_data_new_uint64(xpath, bs->stats.tx_echo_pkt);
-}
-
-/*
- * XPath: /frr-bfdd:bfdd/bfd/sessions/multi-hop
- */
-static int bfdd_bfd_sessions_multi_hop_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return bfd_session_create(event, dnode, resource, true);
-}
-
-static int bfdd_bfd_sessions_multi_hop_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- return bfd_session_destroy(event, dnode, true);
-}
-
-static const void *
-bfdd_bfd_sessions_multi_hop_get_next(const void *parent_list_entry
- __attribute__((__unused__)),
- const void *list_entry)
-{
- return bfd_session_next(list_entry, true);
-}
-
-static int bfdd_bfd_sessions_multi_hop_get_keys(const void *list_entry,
- struct yang_list_keys *keys)
-{
- const struct bfd_session *bs = list_entry;
- char dstbuf[INET6_ADDRSTRLEN], srcbuf[INET6_ADDRSTRLEN];
-
- inet_ntop(bs->key.family, &bs->key.peer, dstbuf, sizeof(dstbuf));
- inet_ntop(bs->key.family, &bs->key.local, srcbuf, sizeof(srcbuf));
-
- keys->num = 4;
- strlcpy(keys->key[0], srcbuf, sizeof(keys->key[0]));
- strlcpy(keys->key[1], dstbuf, sizeof(keys->key[1]));
- strlcpy(keys->key[2], bs->key.ifname, sizeof(keys->key[2]));
- strlcpy(keys->key[3], bs->key.vrfname, sizeof(keys->key[3]));
-
- return NB_OK;
-}
-
-static const void *
-bfdd_bfd_sessions_multi_hop_lookup_entry(const void *parent_list_entry
- __attribute__((__unused__)),
- const struct yang_list_keys *keys)
-{
- const char *source_addr = keys->key[0];
- const char *dest_addr = keys->key[1];
- const char *ifname = keys->key[2];
- const char *vrf = keys->key[3];
- struct sockaddr_any psa, lsa;
- struct bfd_key bk;
-
- strtosa(dest_addr, &psa);
- strtosa(source_addr, &lsa);
- gen_bfd_key(&bk, &psa, &lsa, true, ifname, vrf);
-
- return bfd_key_lookup(bk);
-}
-
-/* clang-format off */
-const struct frr_yang_module_info frr_bfdd_info = {
- .name = "frr-bfdd",
- .nodes = {
- {
- .xpath = "/frr-bfdd:bfdd/bfd",
- .cbs = {
- .create = bfdd_bfd_create,
- .destroy = bfdd_bfd_destroy,
- .cli_show = bfd_cli_show_header,
- .cli_show_end = bfd_cli_show_header_end,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop",
- .cbs = {
- .create = bfdd_bfd_sessions_single_hop_create,
- .destroy = bfdd_bfd_sessions_single_hop_destroy,
- .get_next = bfdd_bfd_sessions_single_hop_get_next,
- .get_keys = bfdd_bfd_sessions_single_hop_get_keys,
- .lookup_entry = bfdd_bfd_sessions_single_hop_lookup_entry,
- .cli_show = bfd_cli_show_single_hop_peer,
- .cli_show_end = bfd_cli_show_peer_end,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/source-addr",
- .cbs = {
- .modify = bfdd_bfd_sessions_single_hop_source_addr_modify,
- .destroy = bfdd_bfd_sessions_single_hop_source_addr_destroy,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/detection-multiplier",
- .cbs = {
- .modify = bfdd_bfd_sessions_single_hop_detection_multiplier_modify,
- .cli_show = bfd_cli_show_mult,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/desired-transmission-interval",
- .cbs = {
- .modify = bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify,
- .cli_show = bfd_cli_show_tx,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/required-receive-interval",
- .cbs = {
- .modify = bfdd_bfd_sessions_single_hop_required_receive_interval_modify,
- .cli_show = bfd_cli_show_rx,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/administrative-down",
- .cbs = {
- .modify = bfdd_bfd_sessions_single_hop_administrative_down_modify,
- .cli_show = bfd_cli_show_shutdown,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/echo-mode",
- .cbs = {
- .modify = bfdd_bfd_sessions_single_hop_echo_mode_modify,
- .cli_show = bfd_cli_show_echo,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/desired-echo-transmission-interval",
- .cbs = {
- .modify = bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify,
- .cli_show = bfd_cli_show_echo_interval,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-discriminator",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-state",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_local_state_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-diagnostic",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_local_diagnostic_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-multiplier",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_local_multiplier_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-discriminator",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_discriminator_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-state",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_state_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-diagnostic",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_diagnostic_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-multiplier",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_multiplier_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-transmission-interval",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_transmission_interval_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-receive-interval",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_receive_interval_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/detection-mode",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_detection_mode_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/last-down-time",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_last_down_time_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/last-up-time",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_last_up_time_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/session-down-count",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_session_down_count_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/session-up-count",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_session_up_count_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/control-packet-input-count",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_input_count_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/control-packet-output-count",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_output_count_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-echo-transmission-interval",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_echo_transmission_interval_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/echo-packet-input-count",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_input_count_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/echo-packet-output-count",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop",
- .cbs = {
- .create = bfdd_bfd_sessions_multi_hop_create,
- .destroy = bfdd_bfd_sessions_multi_hop_destroy,
- .get_next = bfdd_bfd_sessions_multi_hop_get_next,
- .get_keys = bfdd_bfd_sessions_multi_hop_get_keys,
- .lookup_entry = bfdd_bfd_sessions_multi_hop_lookup_entry,
- .cli_show = bfd_cli_show_multi_hop_peer,
- .cli_show_end = bfd_cli_show_peer_end,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/detection-multiplier",
- .cbs = {
- .modify = bfdd_bfd_sessions_single_hop_detection_multiplier_modify,
- .cli_show = bfd_cli_show_mult,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/desired-transmission-interval",
- .cbs = {
- .modify = bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify,
- .cli_show = bfd_cli_show_tx,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/required-receive-interval",
- .cbs = {
- .modify = bfdd_bfd_sessions_single_hop_required_receive_interval_modify,
- .cli_show = bfd_cli_show_rx,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/administrative-down",
- .cbs = {
- .modify = bfdd_bfd_sessions_single_hop_administrative_down_modify,
- .cli_show = bfd_cli_show_shutdown,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/local-discriminator",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/local-state",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_local_state_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/local-diagnostic",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_local_diagnostic_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/local-multiplier",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_local_multiplier_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/remote-discriminator",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_discriminator_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/remote-state",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_state_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/remote-diagnostic",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_diagnostic_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/remote-multiplier",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_multiplier_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/negotiated-transmission-interval",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_transmission_interval_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/negotiated-receive-interval",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_receive_interval_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/detection-mode",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_detection_mode_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/last-down-time",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_last_down_time_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/last-up-time",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_last_up_time_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/session-down-count",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_session_down_count_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/session-up-count",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_session_up_count_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/control-packet-input-count",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_input_count_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/control-packet-output-count",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_output_count_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/negotiated-echo-transmission-interval",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_echo_transmission_interval_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/echo-packet-input-count",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_input_count_get_elem,
- }
- },
- {
- .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/echo-packet-output-count",
- .cbs = {
- .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem,
- }
- },
- {
- .xpath = NULL,
- },
- }
-};
diff --git a/bfdd/control.c b/bfdd/control.c
index c308d647d8..5c5421c041 100644
--- a/bfdd/control.c
+++ b/bfdd/control.c
@@ -774,13 +774,13 @@ static void _control_notify(struct bfd_control_socket *bcs,
control_queue_enqueue(bcs, bcm);
}
-int control_notify(struct bfd_session *bs)
+int control_notify(struct bfd_session *bs, uint8_t notify_state)
{
struct bfd_control_socket *bcs;
struct bfd_notify_peer *bnp;
/* Notify zebra listeners as well. */
- ptm_bfd_notify(bs);
+ ptm_bfd_notify(bs, notify_state);
/*
* PERFORMANCE: reuse the bfd_control_msg allocated data for
diff --git a/bfdd/ptm_adapter.c b/bfdd/ptm_adapter.c
index df48bc2af0..dcca70b796 100644
--- a/bfdd/ptm_adapter.c
+++ b/bfdd/ptm_adapter.c
@@ -153,7 +153,7 @@ static int _ptm_msg_address(struct stream *msg, int family, const void *addr)
return 0;
}
-int ptm_bfd_notify(struct bfd_session *bs)
+int ptm_bfd_notify(struct bfd_session *bs, uint8_t notify_state)
{
struct stream *msg;
@@ -204,12 +204,15 @@ int ptm_bfd_notify(struct bfd_session *bs)
_ptm_msg_address(msg, bs->key.family, &bs->key.peer);
/* BFD status */
- switch (bs->ses_state) {
+ switch (notify_state) {
case PTM_BFD_UP:
stream_putl(msg, BFD_STATUS_UP);
break;
case PTM_BFD_ADM_DOWN:
+ stream_putl(msg, BFD_STATUS_ADMIN_DOWN);
+ break;
+
case PTM_BFD_DOWN:
case PTM_BFD_INIT:
stream_putl(msg, BFD_STATUS_DOWN);
@@ -432,7 +435,7 @@ static void bfdd_dest_register(struct stream *msg, vrf_id_t vrf_id)
return;
}
- ptm_bfd_notify(bs);
+ ptm_bfd_notify(bs, bs->ses_state);
}
static void bfdd_dest_deregister(struct stream *msg, vrf_id_t vrf_id)
@@ -461,6 +464,10 @@ static void bfdd_dest_deregister(struct stream *msg, vrf_id_t vrf_id)
if (bs->refcount ||
BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG))
return;
+
+ bs->ses_state = PTM_BFD_ADM_DOWN;
+ ptm_bfd_snd(bs, 0);
+
ptm_bfd_sess_del(&bpc);
}
diff --git a/bfdd/subdir.am b/bfdd/subdir.am
index 5e3c3d4765..ed1d3962bf 100644
--- a/bfdd/subdir.am
+++ b/bfdd/subdir.am
@@ -13,7 +13,9 @@ endif
bfdd_libbfd_a_SOURCES = \
bfdd/bfd.c \
- bfdd/bfdd_northbound.c \
+ bfdd/bfdd_nb.c \
+ bfdd/bfdd_nb_config.c \
+ bfdd/bfdd_nb_state.c \
bfdd/bfdd_vty.c \
bfdd/bfdd_cli.c \
bfdd/bfd_packet.c \
@@ -32,6 +34,7 @@ bfdd/bfdd_cli.$(OBJEXT): bfdd/bfdd_cli_clippy.c
noinst_HEADERS += \
bfdd/bfdctl.h \
+ bfdd/bfdd_nb.h \
bfdd/bfd.h \
# end
diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c
index 57fef8e913..0ed6057eac 100644
--- a/bgpd/bgp_bfd.c
+++ b/bgpd/bgp_bfd.c
@@ -282,7 +282,8 @@ static void bgp_bfd_peer_status_update(struct peer *peer, int status,
return;
old_status = bfd_info->status;
- bfd_info->status = status;
+ BFD_SET_CLIENT_STATUS(bfd_info->status, status);
+
bfd_info->last_update = bgp_clock();
if (status != old_status) {
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
index f476b16188..0e4c3a3e12 100644
--- a/bgpd/bgp_debug.c
+++ b/bgpd/bgp_debug.c
@@ -209,7 +209,7 @@ static void bgp_debug_list_free(struct list *list)
listnode_delete(list, filter);
if (filter->p)
- prefix_free(filter->p);
+ prefix_free(&filter->p);
if (filter->host)
XFREE(MTYPE_BGP_DEBUG_STR, filter->host);
@@ -323,7 +323,7 @@ static int bgp_debug_list_remove_entry(struct list *list, const char *host,
} else if (p && filter->p->prefixlen == p->prefixlen
&& prefix_match(filter->p, p)) {
listnode_delete(list, filter);
- prefix_free(filter->p);
+ prefix_free(&filter->p);
XFREE(MTYPE_BGP_DEBUG_FILTER, filter);
return 1;
}
@@ -1412,7 +1412,7 @@ DEFPY (debug_bgp_update_prefix_afi_safi,
ret = bgp_debug_parse_evpn_prefix(vty, argv, argc, &argv_p);
if (ret != CMD_SUCCESS) {
- prefix_free(argv_p);
+ prefix_free(&argv_p);
return ret;
}
@@ -1425,7 +1425,7 @@ DEFPY (debug_bgp_update_prefix_afi_safi,
vty_out(vty,
"BGP updates debugging is already enabled for %s\n",
buf);
- prefix_free(argv_p);
+ prefix_free(&argv_p);
return CMD_SUCCESS;
}
@@ -1438,7 +1438,7 @@ DEFPY (debug_bgp_update_prefix_afi_safi,
vty_out(vty, "BGP updates debugging is on for %s\n", buf);
}
- prefix_free(argv_p);
+ prefix_free(&argv_p);
return CMD_SUCCESS;
}
@@ -1477,7 +1477,7 @@ DEFPY (no_debug_bgp_update_prefix_afi_safi,
ret = bgp_debug_parse_evpn_prefix(vty, argv, argc, &argv_p);
if (ret != CMD_SUCCESS) {
- prefix_free(argv_p);
+ prefix_free(&argv_p);
return ret;
}
@@ -1505,7 +1505,7 @@ DEFPY (no_debug_bgp_update_prefix_afi_safi,
vty_out(vty, "BGP updates debugging was not enabled for %s\n",
buf);
- prefix_free(argv_p);
+ prefix_free(&argv_p);
return ret;
}
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
index 3f2f11dbe2..07d3f7b31e 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -2608,9 +2608,10 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
&& (struct bgp_path_info *)pi->extra->parent == parent_pi)
break;
- if (!pi)
- pi = bgp_create_evpn_bgp_path_info(parent_pi, rn);
- else {
+ if (!pi) {
+ /* Create an info */
+ (void)bgp_create_evpn_bgp_path_info(parent_pi, rn);
+ } else {
if (attrhash_cmp(pi->attr, parent_pi->attr)
&& !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) {
bgp_unlock_node(rn);
diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c
index 648c3be47e..d37bf54734 100644
--- a/bgpd/bgp_mpath.c
+++ b/bgpd/bgp_mpath.c
@@ -614,8 +614,6 @@ void bgp_path_info_mpath_update(struct bgp_node *rn,
if ((mpath_count < maxpaths) && (new_mpath != new_best)
&& bgp_path_info_nexthop_cmp(prev_mpath,
new_mpath)) {
- if (new_mpath == next_mpath)
- bgp_path_info_mpath_next(new_mpath);
bgp_path_info_mpath_dequeue(new_mpath);
bgp_path_info_mpath_enqueue(prev_mpath,
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index de4f185ab2..c07fa2516e 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -1639,9 +1639,9 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi,
return 0;
}
-#ifdef BGP_SEND_ASPATH_CHECK
/* AS path loop check. */
- if (onlypeer && aspath_loop_check(piattr->aspath, onlypeer->as)) {
+ if (onlypeer && onlypeer->as_path_loop_detection
+ && aspath_loop_check(piattr->aspath, onlypeer->as)) {
if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
zlog_debug(
"%s [Update:SEND] suppress announcement to peer AS %u "
@@ -1649,7 +1649,6 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi,
onlypeer->host, onlypeer->as);
return 0;
}
-#endif /* BGP_SEND_ASPATH_CHECK */
/* If we're a CONFED we need to loop check the CONFED ID too */
if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) {
@@ -1751,18 +1750,22 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi,
* if
* the peer (group) is configured to receive link-local nexthop
* unchanged
- * and it is available in the prefix OR we're not reflecting the route
- * and
+ * and it is available in the prefix OR we're not reflecting the route,
+ * link-local nexthop address is valid and
* the peer (group) to whom we're going to announce is on a shared
* network
* and this is either a self-originated route or the peer is EBGP.
+ * By checking if nexthop LL address is valid we are sure that
+ * we do not announce LL address as `::`.
*/
if (NEXTHOP_IS_V6) {
attr->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
if ((CHECK_FLAG(peer->af_flags[afi][safi],
PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)
&& IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_local))
- || (!reflect && peer->shared_network
+ || (!reflect
+ && IN6_IS_ADDR_LINKLOCAL(&peer->nexthop.v6_local)
+ && peer->shared_network
&& (from == bgp->peer_self
|| peer->sort == BGP_PEER_EBGP))) {
attr->mp_nexthop_len =
@@ -7241,21 +7244,11 @@ void route_vty_out(struct vty *vty, struct prefix *p,
if (json_paths) {
json_nexthop_global = json_object_new_object();
- if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN))
- json_object_string_add(
- json_nexthop_global,
- nexthop_fqdn ? "fqdn" : "ip",
- nexthop_fqdn
- ? nexthop_fqdn
- : inet_ntoa(
- attr->mp_nexthop_global_in));
- else
- json_object_string_add(
- json_nexthop_global,
- nexthop_fqdn ? "fqdn" : "ip",
- nexthop_fqdn
- ? nexthop_fqdn
- : inet_ntoa(attr->nexthop));
+ json_object_string_add(json_nexthop_global,
+ nexthop_fqdn ? "fqdn" : "ip",
+ nexthop_fqdn
+ ? nexthop_fqdn
+ : inet_ntoa(attr->nexthop));
json_object_string_add(json_nexthop_global, "afi",
"ipv4");
@@ -10555,7 +10548,7 @@ static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp,
}
ret = bgp_show(vty, bgp, afi, safi, type, p, 0);
- prefix_free(p);
+ prefix_free(&p);
return ret;
}
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index eae9db5a61..17c93ffc38 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -159,10 +159,8 @@ static const char *get_afi_safi_vty_str(afi_t afi, safi_t safi)
return "IPv6 Flowspec";
else if (afi == AFI_L2VPN && safi == SAFI_EVPN)
return "L2VPN EVPN";
- else {
- flog_err(EC_LIB_DEVELOPMENT, "New afi/safi that needs to be taken care of?");
+ else
return "Unknown";
- }
}
/*
@@ -199,10 +197,8 @@ static const char *get_afi_safi_json_str(afi_t afi, safi_t safi)
return "ipv6Flowspec";
else if (afi == AFI_L2VPN && safi == SAFI_EVPN)
return "l2VpnEvpn";
- else {
- flog_err(EC_LIB_DEVELOPMENT, "New afi/safi that needs to be taken care of?");
+ else
return "Unknown";
- }
}
/* Utility function to get address family from current node. */
@@ -6385,6 +6381,44 @@ ALIAS_HIDDEN(no_neighbor_addpath_tx_bestpath_per_as,
NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
"Use addpath to advertise the bestpath per each neighboring AS\n")
+DEFPY(
+ neighbor_aspath_loop_detection, neighbor_aspath_loop_detection_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor sender-as-path-loop-detection",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Detect AS loops before sending to neighbor\n")
+{
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, neighbor);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ peer->as_path_loop_detection = true;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(
+ no_neighbor_aspath_loop_detection,
+ no_neighbor_aspath_loop_detection_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor sender-as-path-loop-detection",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Detect AS loops before sending to neighbor\n")
+{
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, neighbor);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ peer->as_path_loop_detection = false;
+
+ return CMD_SUCCESS;
+}
+
static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv,
struct ecommunity **list)
{
@@ -13682,6 +13716,10 @@ void bgp_vty_init(void)
install_element(BGP_VPNV6_NODE,
&no_neighbor_addpath_tx_bestpath_per_as_cmd);
+ /* "neighbor sender-as-path-loop-detection" commands. */
+ install_element(BGP_NODE, &neighbor_aspath_loop_detection_cmd);
+ install_element(BGP_NODE, &no_neighbor_aspath_loop_detection_cmd);
+
/* "neighbor passive" commands. */
install_element(BGP_NODE, &neighbor_passive_cmd);
install_element(BGP_NODE, &no_neighbor_passive_cmd);
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index 7923f076c1..d0a732b153 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -357,7 +357,7 @@ static int bgp_interface_address_delete(ZAPI_CALLBACK_ARGS)
bgp_connected_delete(bgp, ifc);
}
- connected_free(ifc);
+ connected_free(&ifc);
return 0;
}
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 49f7337d76..7621d7ef0f 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -1593,7 +1593,7 @@ struct peer *peer_create(union sockunion *su, const char *conf_if,
peer->readtime = peer->resettime = bgp_clock();
/* Default TTL set. */
- peer->ttl = (peer->sort == BGP_PEER_IBGP) ? MAXTTL : 1;
+ peer->ttl = (peer->sort == BGP_PEER_IBGP) ? MAXTTL : BGP_DEFAULT_TTL;
SET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE);
@@ -1680,7 +1680,7 @@ void peer_as_change(struct peer *peer, as_t as, int as_specified)
if (peer_sort(peer) == BGP_PEER_IBGP)
peer->ttl = MAXTTL;
else if (type == BGP_PEER_IBGP)
- peer->ttl = 1;
+ peer->ttl = BGP_DEFAULT_TTL;
/* reflector-client reset */
if (peer_sort(peer) != BGP_PEER_IBGP) {
@@ -2444,7 +2444,7 @@ struct peer_group *peer_group_get(struct bgp *bgp, const char *name)
group->conf->host = XSTRDUP(MTYPE_BGP_PEER_HOST, name);
group->conf->group = group;
group->conf->as = 0;
- group->conf->ttl = 1;
+ group->conf->ttl = BGP_DEFAULT_TTL;
group->conf->gtsm_hops = 0;
group->conf->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
SET_FLAG(group->conf->sflags, PEER_STATUS_GROUP);
@@ -2469,8 +2469,9 @@ static void peer_group2peer_config_copy(struct peer_group *group,
if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_LOCAL_AS))
peer->change_local_as = conf->change_local_as;
- /* TTL */
- peer->ttl = conf->ttl;
+ /* If peer-group has configured TTL then override it */
+ if (conf->ttl != BGP_DEFAULT_TTL)
+ peer->ttl = conf->ttl;
/* GTSM hops */
peer->gtsm_hops = conf->gtsm_hops;
@@ -2585,7 +2586,7 @@ int peer_group_delete(struct peer_group *group)
for (afi = AFI_IP; afi < AFI_MAX; afi++) {
for (ALL_LIST_ELEMENTS(group->listen_range[afi], node, nnode,
prefix)) {
- prefix_free(prefix);
+ prefix_free(&prefix);
}
list_delete(&group->listen_range[afi]);
}
@@ -4379,7 +4380,7 @@ int peer_ebgp_multihop_unset(struct peer *peer)
if (peer_group_active(peer))
peer->ttl = peer->group->conf->ttl;
else
- peer->ttl = 1;
+ peer->ttl = BGP_DEFAULT_TTL;
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
@@ -4393,7 +4394,7 @@ int peer_ebgp_multihop_unset(struct peer *peer)
if (peer->sort == BGP_PEER_IBGP)
continue;
- peer->ttl = 1;
+ peer->ttl = BGP_DEFAULT_TTL;
if (peer->fd >= 0) {
if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
@@ -7165,6 +7166,11 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
/* strict-capability-match */
if (peergroup_flag_check(peer, PEER_FLAG_STRICT_CAP_MATCH))
vty_out(vty, " neighbor %s strict-capability-match\n", addr);
+
+ /* Sender side AS path loop detection. */
+ if (peer->as_path_loop_detection)
+ vty_out(vty, " neighbor %s sender-as-path-loop-detection\n",
+ addr);
}
/* BGP peer configuration display function. */
@@ -7529,7 +7535,6 @@ static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi,
int bgp_config_write(struct vty *vty)
{
- int write = 0;
struct bgp *bgp;
struct peer_group *group;
struct peer *peer;
@@ -7540,9 +7545,6 @@ int bgp_config_write(struct vty *vty)
vty_out(vty, "bgp route-map delay-timer %u\n",
bm->rmap_update_timer);
- if (write)
- vty_out(vty, "!\n");
-
/* BGP configuration. */
for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 87d94500f7..317f200b85 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -635,6 +635,8 @@ struct bgp_nexthop {
#define RMAP_OUT 1
#define RMAP_MAX 2
+#define BGP_DEFAULT_TTL 1
+
#include "filter.h"
/* BGP filter structure. */
@@ -1223,6 +1225,9 @@ struct peer {
char *hostname;
char *domainname;
+ /* Sender side AS path loop detection. */
+ bool as_path_loop_detection;
+
QOBJ_FIELDS
};
DECLARE_QOBJ_TYPE(peer)
@@ -1445,9 +1450,6 @@ struct bgp_nlri {
#define BGP_VTY_PORT 2605
#define BGP_DEFAULT_CONFIG "bgpd.conf"
-/* Check AS path loop when we send NLRI. */
-/* #define BGP_SEND_ASPATH_CHECK */
-
/* BGP Dynamic Neighbors feature */
#define BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT 100
#define BGP_DYNAMIC_NEIGHBORS_LIMIT_MIN 1
diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c
index 83b05ce536..7c4f8eaa01 100644
--- a/bgpd/rfapi/rfapi.c
+++ b/bgpd/rfapi/rfapi.c
@@ -3190,12 +3190,8 @@ DEFUN (debug_rfapi_register_vn_un_l2o,
return CMD_WARNING_CONFIG_FAILED;
}
optary[opt_next].type = RFAPI_VN_OPTION_TYPE_L2ADDR;
- if (opt_next) {
- optary[opt_next - 1].next = optary + opt_next;
- } else {
- opt = optary;
- }
- ++opt_next;
+ opt = optary;
+
/* L2 option parsing END */
/* TBD fixme */
diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c
index fe8e874440..660442b492 100644
--- a/bgpd/rfapi/rfapi_import.c
+++ b/bgpd/rfapi/rfapi_import.c
@@ -1512,7 +1512,7 @@ static int rfapiNhlAddNodeRoutes(
}
seen_nexthops =
- skiplist_new(0, vnc_prefix_cmp, (void (*)(void *))prefix_free);
+ skiplist_new(0, vnc_prefix_cmp, prefix_free_lists);
for (bpi = rn->info; bpi; bpi = bpi->next) {
@@ -4359,7 +4359,7 @@ rfapiImportTableRefAdd(struct bgp *bgp, struct ecommunity *rt_import_list,
it->rt_import_list = ecommunity_dup(rt_import_list);
it->rfg = rfg;
it->monitor_exterior_orphans =
- skiplist_new(0, NULL, (void (*)(void *))prefix_free);
+ skiplist_new(0, NULL, prefix_free_lists);
/*
* fill import route tables from RIBs
diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c
index dc4a02e8b2..4fa64075c4 100644
--- a/bgpd/rfapi/rfapi_vty.c
+++ b/bgpd/rfapi/rfapi_vty.c
@@ -4708,8 +4708,6 @@ static int vnc_add_vrf_prefix(struct vty *vty, const char *arg_vrf,
rfapiQprefix2Rprefix(&pfx, &rpfx);
memset(optary, 0, sizeof(optary));
if (arg_rd) {
- if (opt != NULL)
- opt->next = &optary[cur_opt];
opt = &optary[cur_opt++];
opt->type = RFAPI_VN_OPTION_TYPE_INTERNAL_RD;
if (!str2prefix_rd(arg_rd, &opt->v.internal_rd)) {
diff --git a/bgpd/rfapi/vnc_import_bgp.c b/bgpd/rfapi/vnc_import_bgp.c
index 51ec999764..6e8969ad18 100644
--- a/bgpd/rfapi/vnc_import_bgp.c
+++ b/bgpd/rfapi/vnc_import_bgp.c
@@ -479,8 +479,6 @@ static void vnc_import_bgp_add_route_mode_resolve_nve_one_bi(
encaptlvs = bpi->attr->vnc_subtlvs;
if (bpi->attr->encap_tunneltype != BGP_ENCAP_TYPE_RESERVED
&& bpi->attr->encap_tunneltype != BGP_ENCAP_TYPE_MPLS) {
- if (opt != NULL)
- opt->next = &optary[cur_opt];
opt = &optary[cur_opt++];
memset(opt, 0, sizeof(struct rfapi_un_option));
opt->type = RFAPI_UN_OPTION_TYPE_TUNNELTYPE;
@@ -1809,8 +1807,7 @@ static void vnc_import_bgp_exterior_add_route_it(
RFAPI_MONITOR_EXTERIOR(rn)->source =
skiplist_new(
0, NULL,
- (void (*)(void *))
- prefix_free);
+ prefix_free_lists);
agg_lock_node(rn); /* for skiplist */
}
agg_lock_node(rn); /* for skiplist entry */
@@ -2196,8 +2193,7 @@ void vnc_import_bgp_exterior_add_route_interior(
->source) {
RFAPI_MONITOR_EXTERIOR(rn_interior)
->source = skiplist_new(
- 0, NULL,
- (void (*)(void *))prefix_free);
+ 0, NULL, prefix_free_lists);
agg_lock_node(rn_interior);
}
skiplist_insert(
@@ -2339,8 +2335,7 @@ void vnc_import_bgp_exterior_add_route_interior(
if (!RFAPI_MONITOR_EXTERIOR(rn_interior)->source) {
RFAPI_MONITOR_EXTERIOR(rn_interior)->source =
skiplist_new(
- 0, NULL,
- (void (*)(void *))prefix_free);
+ 0, NULL, prefix_free_lists);
agg_lock_node(rn_interior); /* sl */
}
skiplist_insert(
@@ -2529,8 +2524,7 @@ void vnc_import_bgp_exterior_del_route_interior(
if (!RFAPI_MONITOR_EXTERIOR(par)->source) {
RFAPI_MONITOR_EXTERIOR(par)->source =
skiplist_new(
- 0, NULL,
- (void (*)(void *))prefix_free);
+ 0, NULL, prefix_free_lists);
agg_lock_node(par); /* sl */
}
skiplist_insert(RFAPI_MONITOR_EXTERIOR(par)->source,
diff --git a/debian/copyright b/debian/copyright
index d1f28a65a2..edd73020bd 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -12,6 +12,7 @@ Copyright: 1996-2003 by the original Zebra authors:
2016-2018 by the FRRouting Project
Adam Fitzgerald 2017
Alex Couloumbis 2017
+ Alexandre Cassen 2001-2017
Alexandre Chappuis 2011
Alexis Fasquel 2015
Ali Rezaee 2018
@@ -47,6 +48,7 @@ Copyright: 1996-2003 by the original Zebra authors:
Christoffer Hansen 2018
Christoph Dwertmann 2018
Colin Petrie 2016
+ Cumulus Networks 2013-2019
Daniel Kozlowski 2012
Daniel Ng 2008
Daniel Walton 2015-2018
@@ -153,6 +155,7 @@ Copyright: 1996-2003 by the original Zebra authors:
Olivier Cochard-Labbé 2014
Olivier Dugeon 2014-2018
Ondrej Zajicek 2009
+ Open Source Routing / NetDEF 2012-2017
Pascal Mathis 2018
Paul Jakma 2002-2016
Paul P Komkoff Jr 2008
@@ -266,7 +269,7 @@ Copyright:
Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
Copyright 2007, 2008 by Grégoire Henry, Julien Cristau and Juliusz Chroboczek
-Files: babeld/babel_errors.* babeld/babel_memory.*
+Files: babeld/babel_errors.*
License: GPL-2+
Copyright: Copyright (C) 2017-2018 Donald Sharp, Cumulus Networks, Inc.
diff --git a/doc/developer/frr-release-procedure.rst b/doc/developer/frr-release-procedure.rst
new file mode 100644
index 0000000000..ff95aa04a9
--- /dev/null
+++ b/doc/developer/frr-release-procedure.rst
@@ -0,0 +1,181 @@
+.. _frr-release-procedure:
+
+FRR Release Procedure
+=====================
+
+``<version>`` - version to be released, e.g. 7.3
+``origin`` - FRR upstream repository
+
+1. Checkout ``dev/<version>``.
+
+ .. code-block:: console
+
+ git checkout dev/<version>
+
+2. Create and push a new branch called ``stable/<version>`` based on the
+ ``dev/<version>`` branch.
+
+ .. code-block:: console
+
+ git checkout -b stable/<version>
+ git push origin stable/<version>:refs/heads/stable/<version>
+
+3. Update Changelog for Red Hat Packages:
+
+ Edit :file:`redhat/frr.spec.in` and look for the ``%changelog`` section:
+
+ - Change last (top of list) entry from ``%{version}`` to the **last**
+ released version number. For example, if ``<version>`` is ``7.3`` and the
+ last public release was ``7.2``, you would use ``7.2``, changing the file
+ like so::
+
+ * Tue Nov 7 2017 Martin Winter <mwinter@opensourcerouting.org> - %{version}
+
+ to::
+
+ * Tue Nov 7 2017 Martin Winter <mwinter@opensourcerouting.org> - 7.2
+
+ - Add new entry to the top of the list with ``%{version}`` tag. Make sure
+ to watch the format, i.e. the day is always 2 characters, with the 1st
+ character being a space if the day is one digit.
+
+ - Add the changelog text below this entry.
+
+4. Update Changelog for Debian Packages:
+
+ Edit :file:`changelog-auto.in`:
+
+ - Change last (top of list) entry from ``@VERSION@`` to the **last**
+ released version number. For example, if ``<version>`` is ``7.3`` and the
+ last public release was ``7.2``, you would use ``7.2``, changing the file
+ like so::
+
+ frr (@VERSION@) RELEASED; urgency=medium
+
+ to::
+
+ frr (7.2) RELEASED; urgency=medium
+
+ - Add a new entry to the top of the list with a ``@VERSION@`` tag. Make sure
+ to watch the format.
+
+ - Add the changelog text below this entry.
+
+ - Verify the changelog format using ``dpkg-parsechangelog``. In the
+ repository root:
+
+ .. code-block:: console
+
+ dpkg-parsechangelog
+
+ You should see output like this::
+
+ vagrant@local ~/frr> dpkg-parsechangelog
+ Source: frr
+ Version: 7.3-dev-0
+ Distribution: UNRELEASED
+ Urgency: medium
+ Maintainer: FRRouting-Dev <dev@lists.frrouting.org>
+ Timestamp: 1540478210
+ Date: Thu, 25 Oct 2018 16:36:50 +0200
+ Changes:
+ frr (7.3-dev-0) RELEASED; urgency=medium
+ .
+ * Your Changes Here
+
+5. Change main version number:
+
+ - Edit :file:`configure.ac` and change version in the ``AC_INIT`` command
+ to ``<version>``
+
+6. Commit the changes, adding the changelog to the commit message. Follow all
+ existing commit guidelines.
+
+7. Create and submit a GitHub pull request, with the ``HEAD`` set to
+ ``stable/<version>`` and the base set to the upstream ``master`` branch.
+ Allow NetDef CI to complete its run and verify that all package builds were
+ successful.
+
+8. Create a git tag for the version:
+
+ .. code-block:: console
+
+ git tag -a frr-<version> -m "FRRouting Release <version>"
+
+9. Push the commit and new tag.
+
+ .. code-block:: console
+
+ git push origin stable/<version>:refs/head/stable/<version>
+ git push origin frr-<version>
+
+10. Kick off the Release build plan on the CI system for the correct release.
+ Contact Martin Winter for this step. Ensure all release packages build
+ successfully.
+
+11. Kick off the Snapcraft build plan for the release.
+
+12. Acquire the release RPM binary packages from Martin Winter.
+
+13. On GitHub, go to the <https://github.com/FRRouting/frr/releases>_ and click
+ "Draft a new release". Write a release announcement. The release
+ announcement should follow the template in
+ ``release-announcement-template.md``, located next to this document. Check
+ for spelling errors, and optionally (but preferably) have other maintainers
+ proofread the announcement text.
+
+ Attach **only** the binary RPM packages to the GitHub release using
+ GitHub's attachment functionality. Do not attach Debian packages. Do not
+ attach source tarballs - these will be generated and attached by GitHub
+ automatically. Do not publish the release yet.
+
+14. Contact the current Debian maintainer for FRR to get new Debian packages
+ built and published on our APT repository at https://deb.frrouting.net/.
+ Ensure the webpage text is updated. Verify that new packages install
+ successfully on a vanilla Debian installation using the instructions on the
+ webpage.
+
+15. Deploy Snapcraft release (after CI system finishes the tests for snapcraft
+ testplan).
+
+16. Update the Read The Docs instance to being publishing documentation built
+ off the ``stable/<version>`` branch. Contact Quentin Young for this step.
+
+17. Publish the GitHub release.
+
+18. Clone the ``frr-www`` repository:
+
+ .. code-block:: console
+
+ git clone https://github.com/FRRouting/frr-www.git
+
+19. Add a new release announcement, using a previous announcement as template:
+
+ .. code-block:: console
+
+ cp <old-version>-launch.md <version>-launch.md
+
+ Paste the GitHub release announcement text into this document, and **remove
+ line breaks**. In other words, this::
+
+ This is one continuous
+ sentence that should be
+ rendered on one line
+
+ Needs to be changed to this::
+
+ This is one continuous sentence that should be rendered on one line
+
+ This is very important otherwise the announcement will be unreadable on the
+ website.
+
+ Make sure to add a link to the GitHub releases page at the top.
+
+ Once finished, manually add a new entry into ``index.html`` to link to this
+ new announcement. Look at past commits to see how to do this.
+
+20. Deploy the updated ``frr-www`` on the frrouting.org web server and verify
+ that the announcement text is visible.
+
+21. Send an email to ``announce@lists.frrouting.org``. The text of this email
+ should include the text from the GitHub release.
diff --git a/doc/developer/maintainer-release-build.rst b/doc/developer/maintainer-release-build.rst
deleted file mode 100644
index 7792173034..0000000000
--- a/doc/developer/maintainer-release-build.rst
+++ /dev/null
@@ -1,89 +0,0 @@
-Release Build Procedure for FRR Maintainers
-===========================================
-
-1. Rename branch (if needed)
-
-.. code-block:: shell
-
- git clone git@github.com:FRRouting/frr.git
- cd frr
- git checkout dev/5.0
- git push origin :refs/heads/dev/5.0
- git push origin dev/5.0:refs/heads/stable/5.0
-
-2. Checkout the new stable branch:
-
-.. code-block:: shell
-
- git checkout stable/5.0
-
-3. Update Changelog for RedHat Package:
-
- Edit :file:`redhat/frr.spec.in` and look for the ``%changelog`` section:
-
- - Change last (top of list) entry from ``%{version}`` to previous fixed
- version number, i.e.::
-
- * Tue Nov 7 2017 Martin Winter <mwinter@opensourcerouting.org> - %{version}
-
- to::
-
- * Tue Nov 7 2017 Martin Winter <mwinter@opensourcerouting.org> - 3.0.2
-
- - Add new entry to the top of the list with ``%{version}`` tag and changelog
- for version.
- Make sure to watch the format, i.e. the day is always 2 characters, with
- the 1st character being a space if the day is one digit.
-
-4. Update Changelog for Debian Packages:
-
- Edit :file:`debian/changelog-auto.in`:
-
- - Change last (top of list) entry from ``@VERSION@`` to previous fixed
- version number, i.e.::
-
- frr (@VERSION@) RELEASED; urgency=medium
-
- to::
-
- frr (3.0.2) RELEASED; urgency=medium
-
- - Add a new entry to the top of the list with a ``@VERSION@`` tag and
- changelog for version.
-
-5. Change main version number:
-
- - Edit :file:`configure.ac` and change version in the ``AC_INIT`` command
- - Create a new entry with the version as ``%{version}`` tag
-
-6. Test building at least a Red Hat and Ubuntu package (or create a PR to have
- the CI system test them)
-
-7. Commit the changes, adding the changelog to the commit message
-
-8. Create a git tag for the version:
-
- .. code-block:: shell
-
- git tag -a frr-5.0 -m "FRRouting Release 5.0"
-
-9. Push the commit and tag(s) and watch for errors on CI:
-
- .. code-block:: shell
-
- git push
- git push --tags
-
-10. Kick off the Release build plan on the CI system for the correct release
-
-11. Send a Release Announcement with changes to
- ``announce@lists.frrouting.org``
-
-12. Kick off the Snapcraft build plan for the correct release
-
-13. After CI plans succeed, release on GitHub by going to
- https://github.com/FRRouting/frr/releases and selecting "Draft a new
- release".
-
-14. Deploy Snapcraft release (after CI system finishes the tests for snapcraft
- testplan)
diff --git a/doc/developer/packaging.rst b/doc/developer/packaging.rst
index b174a9660c..0c072e4d16 100644
--- a/doc/developer/packaging.rst
+++ b/doc/developer/packaging.rst
@@ -1,10 +1,10 @@
-*********
-Packaging
-*********
+********************
+Releases & Packaging
+********************
.. toctree::
:maxdepth: 2
- maintainer-release-build
+ frr-release-procedure
packaging-debian
packaging-redhat
diff --git a/doc/developer/release-announcement-template.md b/doc/developer/release-announcement-template.md
new file mode 100644
index 0000000000..658b87eada
--- /dev/null
+++ b/doc/developer/release-announcement-template.md
@@ -0,0 +1,40 @@
+<!---
+name: release-announcement-template
+about: Template to use when drafing a new release announcement. DELETE THIS
+ BLOCK BEFORE PUBLISHING.
+--->
+
+We are pleased to announce FRR <version>.
+
+<!-- Add a brief summary of major changes here -->
+
+Thank you to all contributors!
+
+Changelog
+---------
+
+<!-- List **only** user-visible changes in this section. When listing changes to individual daemons, alphabetize the list by daemon name. -->
+
+**All daemons:**
+- <!-- List changes to all daemons -->
+
+<!-- If a new daemon was added, list it at the top here -->
+**New daemon: <new>**
+- Adds support for <protocol/feature>
+
+**daemon 1**
+- <!-- List changes -->
+
+**daemon 2**
+- <!-- List changes -->
+
+**daemon N**
+- <!-- List changes -->
+
+### Internal improvements
+
+- <!-- List **only** user-invisible changes here -->
+
+### Packaging changes
+
+- <!-- List any new or removed packages here -->
diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am
index 557a41c51f..2c49d6b875 100644
--- a/doc/developer/subdir.am
+++ b/doc/developer/subdir.am
@@ -26,6 +26,7 @@ dev_RSTFILES = \
doc/developer/building.rst \
doc/developer/cli.rst \
doc/developer/conf.py \
+ doc/developer/frr-release-procedure.rst \
doc/developer/hooks.rst \
doc/developer/include-compile.rst \
doc/developer/index.rst \
@@ -33,7 +34,6 @@ dev_RSTFILES = \
doc/developer/lists.rst \
doc/developer/locking.rst \
doc/developer/logging.rst \
- doc/developer/maintainer-release-build.rst \
doc/developer/memtypes.rst \
doc/developer/modules.rst \
doc/developer/next-hop-tracking.rst \
@@ -55,6 +55,7 @@ EXTRA_DIST += \
$(dev_RSTFILES) \
doc/developer/draft-zebra-00.ms \
doc/developer/ldpd-basic-test-setup.md \
+ doc/developer/release-announcement-template.md \
# end
DEVBUILD = doc/developer/_build
diff --git a/doc/developer/testing.rst b/doc/developer/testing.rst
index 6396faf9a6..5865a6becc 100644
--- a/doc/developer/testing.rst
+++ b/doc/developer/testing.rst
@@ -8,3 +8,4 @@ Testing
:maxdepth: 2
topotests
+ topotests-jsontopo
diff --git a/doc/developer/topotests-jsontopo.rst b/doc/developer/topotests-jsontopo.rst
index 65bdcbe9cf..bbae80f11d 100644
--- a/doc/developer/topotests-jsontopo.rst
+++ b/doc/developer/topotests-jsontopo.rst
@@ -1,376 +1,371 @@
-.. role:: raw-html-m2r(raw)
- :format: html
+.. _topotests-json:
-*************************************
-FRRouting Topology Tests with Mininet
-*************************************
+Topotests with JSON
+===================
Overview
-########
+--------
On top of current topotests framework following enhancements are done:
-#.
- Creating the topology and assigning IPs to router' interfaces dynamically.\ :raw-html-m2r:`<br>`
- It is achieved by using json file, in which user specify the number of routers,
- links to each router, interfaces for the routers and protocol configurations for
- all routers.
+* Creating the topology and assigning IPs to router' interfaces dynamically.
+ It is achieved by using json file, in which user specify the number of
+ routers, links to each router, interfaces for the routers and protocol
+ configurations for all routers.
-#.
- Creating the configurations dynamically. It is achieved by using
- /usr/lib/frr/frr-reload.py utility, which takes running configuration and the
- newly created configuration for any particular router and creates a delta
- file(diff file) and loads it to router.
+* Creating the configurations dynamically. It is achieved by using
+ :file:`/usr/lib/frr/frr-reload.py` utility, which takes running configuration
+ and the newly created configuration for any particular router and creates a
+ delta file(diff file) and loads it to router.
Logging of test case executions
-###############################
+-------------------------------
-
-#. User can enable logging of testcases execution messages into log file by
- adding "frrtest_log_dir = /tmp/topotests/" in pytest.ini file
-#. Router's current configuration can be displyed on console or sent to logs by
- adding "show_router_config = True" in pytest.ini file
+* The user can enable logging of testcases execution messages into log file by
+ adding ``frrtest_log_dir = /tmp/topotests/`` in :file:`pytest.ini`.
+* Router's current configuration can be displyed on console or sent to logs by
+ adding ``show_router_config = True`` in :file:`pytest.ini`.
Log file name will be displayed when we start execution:
-root@test:~/topotests/example-topojson-test/test_topo_json_single_link# python
-test_topo_json_single_link.py Logs will be sent to logfile:
-/tmp/topotests/test_topo_json_single_link_11:57:01.353797
+
+.. code-block:: console
+
+ root@test:# python ./test_topo_json_single_link.py
+
+ Logs will be sent to logfile:
+ /tmp/topotests/test_topo_json_single_link_11:57:01.353797
Note: directory "/tmp/topotests/" is created by topotests by default, making
use of same directory to save execution logs.
-
Guidelines
-##########
+----------
Writing New Tests
-=================
+^^^^^^^^^^^^^^^^^
-
-This section will guide you in all recommended steps to produce a standard topology test.
+This section will guide you in all recommended steps to produce a standard
+topology test.
This is the recommended test writing routine:
-
* Create a json file , which will have routers and protocol configurations
* Create topology from json
* Create configuration from json
* Write the tests
* Create a Pull Request
+
File Hierarchy
-==============
+^^^^^^^^^^^^^^
Before starting to write any tests one must know the file hierarchy. The
repository hierarchy looks like this:
-.. code-block::
-
- $ cd path/to/topotests
- $ find ./*
- ...
- ./example-topojson-test # the basic example test topology-1
- ./example-topojson-test/test_example_topojson.json # input json file, having
- topology, interfaces, bgp and other configuration
- ./example-topojson-test/test_example_topojson.py # test script to write and
- execute testcases
- ...
- ./lib # shared test/topology functions
- ./lib/topojson.py # library to create topology and configurations dynamically
- from json file
- ./lib/common_config.py # library to create protocol's common configurations ex-
- static_routes, prefix_lists, route_maps etc.
- ./lib/bgp.py # library to create only bgp configurations
+.. code-block:: console
+
+ $ cd path/to/topotests
+ $ find ./*
+ ...
+ ./example-topojson-test # the basic example test topology-1
+ ./example-topojson-test/test_example_topojson.json # input json file, having
+ topology, interfaces, bgp and other configuration
+ ./example-topojson-test/test_example_topojson.py # test script to write and
+ execute testcases
+ ...
+ ./lib # shared test/topology functions
+ ./lib/topojson.py # library to create topology and configurations dynamically
+ from json file
+ ./lib/common_config.py # library to create protocol's common configurations ex-
+ static_routes, prefix_lists, route_maps etc.
+ ./lib/bgp.py # library to create only bgp configurations
Defining the Topology and initial configuration in JSON file
-""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The first step to write a new test is to define the topology and initial
configuration. User has to define topology and initial configuration in JSON
-file. Here is an example of JSON file.
-
-.. code-block::
-
- BGP neihghborship with single phy-link, sample JSON file:
- {
- "ipv4base": "192.168.0.0",
- "ipv4mask": 30,
- "ipv6base": "fd00::",
- "ipv6mask": 64,
- "link_ip_start": {"ipv4": "192.168.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64},
- "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128},
- "routers": {
- "r1": {
- "links": {
- "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
- "r2": {"ipv4": "auto", "ipv6": "auto"},
- "r3": {"ipv4": "auto", "ipv6": "auto"}
- },
- "bgp": {
- "local_as": "64512",
- "address_family": {
- "ipv4": {
- "unicast": {
- "neighbor": {
- "r2": {
- "dest_link": {
- "r1": {}
- }
- },
- "r3": {
- "dest_link": {
- "r1": {}
- }
- }
- }
- }
- }
- }
- }
- },
- "r2": {
- "links": {
- "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
- "r1": {"ipv4": "auto", "ipv6": "auto"},
- "r3": {"ipv4": "auto", "ipv6": "auto"}
- },
- "bgp": {
- "local_as": "64512",
- "address_family": {
- "ipv4": {
- "unicast": {
- "redistribute": [
- {
- "redist_type": "static"
- }
- ],
- "neighbor": {
- "r1": {
- "dest_link": {
- "r2": {}
- }
- },
- "r3": {
- "dest_link": {
- "r2": {}
- }
- }
- }
- }
- }
- }
- }
- }
- ...
-
-
- BGP neighboship with loopback interface, sample JSON file:
- {
- "ipv4base": "192.168.0.0",
- "ipv4mask": 30,
- "ipv6base": "fd00::",
- "ipv6mask": 64,
- "link_ip_start": {"ipv4": "192.168.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64},
- "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128},
- "routers": {
- "r1": {
- "links": {
- "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback",
- "add_static_route":"yes"},
- "r2": {"ipv4": "auto", "ipv6": "auto"}
- },
- "bgp": {
- "local_as": "64512",
- "address_family": {
- "ipv4": {
- "unicast": {
- "neighbor": {
- "r2": {
- "dest_link": {
- "lo": {
- "source_link": "lo"
- }
- }
- }
- }
- }
- }
- }
- },
- "static_routes": [
- {
- "network": "1.0.2.17/32",
- "next_hop": "192.168.0.1
- }
- ]
- },
- "r2": {
- "links": {
- "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback",
- "add_static_route":"yes"},
- "r1": {"ipv4": "auto", "ipv6": "auto"},
- "r3": {"ipv4": "auto", "ipv6": "auto"}
- },
- "bgp": {
- "local_as": "64512",
- "address_family": {
- "ipv4": {
- "unicast": {
- "redistribute": [
- {
- "redist_type": "static"
- }
- ],
- "neighbor": {
- "r1": {
- "dest_link": {
- "lo": {
- "source_link": "lo"
- }
- }
- },
- "r3": {
- "dest_link": {
- "lo": {
- "source_link": "lo"
- }
- }
- }
- }
- }
- }
- }
- },
- "static_routes": [
- {
- "network": "192.0.20.1/32",
- "no_of_ip": 9,
- "admin_distance": 100,
- "next_hop": "192.168.0.1",
- "tag": 4001
- }
- ],
- }
- ...
-
- BGP neighborship with Multiple phy-links, sample JSON file:
- {
- "ipv4base": "192.168.0.0",
- "ipv4mask": 30,
- "ipv6base": "fd00::",
- "ipv6mask": 64,
- "link_ip_start": {"ipv4": "192.168.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64},
- "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128},
- "routers": {
- "r1": {
- "links": {
- "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
- "r2-link1": {"ipv4": "auto", "ipv6": "auto"},
- "r2-link2": {"ipv4": "auto", "ipv6": "auto"}
- },
- "bgp": {
- "local_as": "64512",
- "address_family": {
- "ipv4": {
- "unicast": {
- "neighbor": {
- "r2": {
- "dest_link": {
- "r1-link1": {}
- }
- }
- }
- }
- }
- }
- }
- },
- "r2": {
- "links": {
- "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
- "r1-link1": {"ipv4": "auto", "ipv6": "auto"},
- "r1-link2": {"ipv4": "auto", "ipv6": "auto"},
- "r3-link1": {"ipv4": "auto", "ipv6": "auto"},
- "r3-link2": {"ipv4": "auto", "ipv6": "auto"}
- },
- "bgp": {
- "local_as": "64512",
- "address_family": {
- "ipv4": {
- "unicast": {
- "redistribute": [
- {
- "redist_type": "static"
- }
- ],
- "neighbor": {
- "r1": {
- "dest_link": {
- "r2-link1": {}
- }
- },
- "r3": {
- "dest_link": {
- "r2-link1": {}
- }
- }
- }
- }
- }
- }
- }
- }
- ...
-
-
-JSON file explained
+file. Here is an example of JSON file::
+
+ BGP neihghborship with single phy-link, sample JSON file:
+ {
+ "ipv4base": "192.168.0.0",
+ "ipv4mask": 30,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {"ipv4": "192.168.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64},
+ "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128},
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "64512",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "64512",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ...
+
+
+BGP neighboship with loopback interface, sample JSON file::
+
+ {
+ "ipv4base": "192.168.0.0",
+ "ipv4mask": 30,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {"ipv4": "192.168.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64},
+ "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128},
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback",
+ "add_static_route":"yes"},
+ "r2": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "64512",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "lo": {
+ "source_link": "lo"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "static_routes": [
+ {
+ "network": "1.0.2.17/32",
+ "next_hop": "192.168.0.1
+ }
+ ]
+ },
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback",
+ "add_static_route":"yes"},
+ "r1": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "64512",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "lo": {
+ "source_link": "lo"
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "lo": {
+ "source_link": "lo"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "static_routes": [
+ {
+ "network": "192.0.20.1/32",
+ "no_of_ip": 9,
+ "admin_distance": 100,
+ "next_hop": "192.168.0.1",
+ "tag": 4001
+ }
+ ],
+ }
+ ...
+
+BGP neighborship with Multiple phy-links, sample JSON file::
+
+ {
+ "ipv4base": "192.168.0.0",
+ "ipv4mask": 30,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {"ipv4": "192.168.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64},
+ "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128},
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r2-link2": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "64512",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r1-link2": {"ipv4": "auto", "ipv6": "auto"},
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r3-link2": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "64512",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ...
+
+
+JSON File Explained
"""""""""""""""""""
Mandatory keywords/options in JSON:
-
-* "ipv4base" : base ipv4 address to generate ips, ex - 192.168.0.0
-* "ipv4mask" : mask for ipv4 address, ex - 30
-* "ipv6base" : base ipv6 address to generate ips, ex - fd00:
-* "ipv6mask" : mask for ipv6 address, ex - 64
-* "link_ip_start" : physical interface base ipv4 and ipv6 address
-* "lo_prefix" : loopback interface base ipv4 and ipv6 address
-* "routers" : user can add number of routers as per topology, router's name
+* ``ipv4base`` : base ipv4 address to generate ips, ex - 192.168.0.0
+* ``ipv4mask`` : mask for ipv4 address, ex - 30
+* ``ipv6base`` : base ipv6 address to generate ips, ex - fd00:
+* ``ipv6mask`` : mask for ipv6 address, ex - 64
+* ``link_ip_start`` : physical interface base ipv4 and ipv6 address
+* ``lo_prefix`` : loopback interface base ipv4 and ipv6 address
+* ``routers`` : user can add number of routers as per topology, router's name
can be any logical name, ex- r1 or a0.
-* "r1" : name of the router
-* "lo" : loopback interface dict, ipv4 and/or ipv6 addresses generated automatically
-* "type" : type of interface, to identify loopback interface
-* "links" : physical interfaces dict, ipv4 and/or ipv6 addresses generated
+* ``r1`` : name of the router
+* ``lo`` : loopback interface dict, ipv4 and/or ipv6 addresses generated automatically
+* ``type`` : type of interface, to identify loopback interface
+* ``links`` : physical interfaces dict, ipv4 and/or ipv6 addresses generated
automatically
-* "r2-link1" : it will be used when routers have multiple links. 'r2' is router
+* ``r2-link1`` : it will be used when routers have multiple links. 'r2' is router
name, 'link' is any logical name, '1' is to identify link number,
- router name and link must be seperated by hyphen ("-"), ex- a0-peer1
+ router name and link must be seperated by hyphen (``-``), ex- a0-peer1
Optional keywords/options in JSON:
-* "bgp" : bgp configuration
-* "local_as" : Local AS number
-* "unicast" : All SAFI configuration
-* "neighbor": All neighbor details
-* "dest_link" : Destination link to which router will connect
-* "router_id" : bgp router-id
-* "source_link" : if user wants to establish bgp neighborship with loopback
- interface, add "source_link": "lo"
-* "keepalivetimer" : Keep alive timer for BGP neighbor
-* "holddowntimer" : Hold down timer for BGP neighbor
-* "static_routes" : create static routes for routers
-* "redistribute" : redistribute static and/or connected routes
-* "prefix_lists" : create Prefix-lists for routers
+* ``bgp`` : bgp configuration
+* ``local_as`` : Local AS number
+* ``unicast`` : All SAFI configuration
+* ``neighbor``: All neighbor details
+* ``dest_link`` : Destination link to which router will connect
+* ``router_id`` : bgp router-id
+* ``source_link`` : if user wants to establish bgp neighborship with loopback
+ interface, add ``source_link``: ``lo``
+* ``keepalivetimer`` : Keep alive timer for BGP neighbor
+* ``holddowntimer`` : Hold down timer for BGP neighbor
+* ``static_routes`` : create static routes for routers
+* ``redistribute`` : redistribute static and/or connected routes
+* ``prefix_lists`` : create Prefix-lists for routers
Building topology and configurations
""""""""""""""""""""""""""""""""""""
Topology and initial configuration will be created in setup_module(). Following
-is the sample code:
-
-.. code-block::
+is the sample code::
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
@@ -438,38 +433,38 @@ to be used to reference the routers configuration file location
Example:
-* The topology class that inherits from Mininet Topo class
+* The topology class that inherits from Mininet Topo class;
-.. code-block::
+ .. code-block:: python
- class TemplateTopo(Topo):
- def build(self, *_args, **_opts):
- tgen = get_topogen(self)
- # topology build code
+ class TemplateTopo(Topo):
+ def build(self, *_args, **_opts):
+ tgen = get_topogen(self)
+ # topology build code
-* pytest setup_module() and teardown_module() to start the topology
+* pytest setup_module() and teardown_module() to start the topology:
-.. code-block::
+ .. code-block:: python
- def setup_module(_m):
- tgen = Topogen(TemplateTopo)
+ def setup_module(_m):
+ tgen = Topogen(TemplateTopo)
- # Starting topology, create tmp files which are loaded to routers
- # to start deamons and then start routers
- start_topology(tgen, CWD)
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, CWD)
- def teardown_module(_m):
- tgen = get_topogen()
+ def teardown_module(_m):
+ tgen = get_topogen()
- # Stop toplogy and Remove tmp files
- stop_topology(tgen, CWD)
+ # Stop toplogy and Remove tmp files
+ stop_topology(tgen, CWD)
-* __main__ initialization code (to support running the script directly)
+* ``__main__`` initialization code (to support running the script directly)
-.. code-block::
+ .. code-block:: python
- if **name** == '\ **main**\ ':
- sys.exit(pytest.main(["-s"]))
+ if **name** == '\ **main**\ ':
+ sys.exit(pytest.main(["-s"]))
diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst
index 93d01bfbc1..6eef7532b3 100644
--- a/doc/developer/workflow.rst
+++ b/doc/developer/workflow.rst
@@ -113,6 +113,9 @@ Security fixes are backported to all releases less than or equal to at least one
year old. Security fixes may also be backported to older releases depending on
severity.
+For detailed instructions on how to produce an FRR release, refer to
+:ref:`frr-release-procedure`.
+
Long term support branches ( LTS )
-----------------------------------------
@@ -160,6 +163,34 @@ releases have support for this feature request. Moreover, introducing features
requests may result in breaking the stability of the branch. LTS branches are first
done to bring long term support for stability.
+Development Branches
+--------------------
+
+Occassionally the community will desire the ability to work together
+on a feature that is considered useful to FRR. In this case the
+parties may ask the Maintainers for the creation of a development
+branch in the main FRR repository. Requirements for this to happen
+are:
+
+- A one paragraph description of the feature being implemented to
+ allow for the facilitation of discussion about the feature. This
+ might include pointers to relevant RFC's or presentations that
+ explain what is planned. This is intended to set a somewhat
+ low bar for organization.
+- A branch maintainer must be named. This person is responsible for
+ keeping the branch up to date, and general communication about the
+ project with the other FRR Maintainers. Additionally this person
+ must already be a FRR Maintainer.
+- Commits to this branch must follow the normal PR and commit process
+ as outlined in other areas of this document. The goal of this is
+ to prevent the current state where large features are submitted
+ and are so large they are difficult to review.
+
+After a development branch has completed the work together, a final
+review can be made and the branch merged into master. If a development
+branch is becomes un-maintained or not being actively worked on after
+three months then the Maintainers can decide to remove the branch.
+
Changelog
---------
The changelog will be the base for the release notes. A changelog entry for
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index b916fcf413..6c20658214 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -836,11 +836,16 @@ Redistribution
Redistribute OSPF route to BGP process.
-.. index:: redistribute vpn
-.. clicmd:: redistribute vpn
+.. index:: redistribute vnc
+.. clicmd:: redistribute vnc
Redistribute VNC routes to BGP process.
+.. index:: redistribute vnc-direct
+.. clicmd:: redistribute vnc-direct
+
+ Redistribute VNC direct (not via zebra) routes to BGP process.
+
.. index:: update-delay MAX-DELAY
.. clicmd:: update-delay MAX-DELAY
@@ -1137,6 +1142,14 @@ Peer Filtering
on reflected routes. This option allows the modifications to be reflected as
well. Once enabled, it affects all reflected routes.
+.. index:: [no] neighbor PEER sender-as-path-loop-detection
+.. clicmd:: [no] neighbor PEER sender-as-path-loop-detection
+
+ Enable the detection of sender side AS path loops and filter the
+ bad routes before they are sent.
+
+ This setting is disabled by default.
+
.. _bgp-peer-group:
Peer Groups
diff --git a/doc/user/filter.rst b/doc/user/filter.rst
index 8c86d06087..98b768412d 100644
--- a/doc/user/filter.rst
+++ b/doc/user/filter.rst
@@ -9,11 +9,24 @@ defined, it can be applied in any direction.
IP Access List
==============
-.. index:: access-list NAME permit IPV4-NETWORK
-.. clicmd:: access-list NAME permit IPV4-NETWORK
+.. index:: access-list NAME [seq (1-4294967295)] permit IPV4-NETWORK
+.. clicmd:: access-list NAME [seq (1-4294967295)] permit IPV4-NETWORK
-.. index:: access-list NAME deny IPV4-NETWORK
-.. clicmd:: access-list NAME deny IPV4-NETWORK
+.. index:: access-list NAME [seq (1-4294967295)] deny IPV4-NETWORK
+.. clicmd:: access-list NAME [seq (1-4294967295)] deny IPV4-NETWORK
+
+ seq
+ seq `number` can be set either automatically or manually. In the
+ case that sequential numbers are set manually, the user may pick any
+ number less than 4294967295. In the case that sequential number are set
+ automatically, the sequential number will increase by a unit of five (5)
+ per list. If a list with no specified sequential number is created
+ after a list with a specified sequential number, the list will
+ automatically pick the next multiple of five (5) as the list number.
+ For example, if a list with number 2 already exists and a new list with
+ no specified number is created, the next list will be numbered 5. If
+ lists 2 and 7 already exist and a new list with no specified number is
+ created, the new list will be numbered 10.
Basic filtering is done by `access-list` as shown in the
following example.
@@ -22,6 +35,7 @@ IP Access List
access-list filter deny 10.0.0.0/9
access-list filter permit 10.0.0.0/8
+ access-list filter seq 13 permit 10.0.0.0/7
IP Prefix List
diff --git a/doc/user/overview.rst b/doc/user/overview.rst
index 5f9a7b937e..24d9ece93b 100644
--- a/doc/user/overview.rst
+++ b/doc/user/overview.rst
@@ -230,6 +230,8 @@ features with system dependencies are included here.
+-----------------------------------+----------------+--------------+------------+------------+------------+
| EVPN BUM Forwarding | :mark:`≥5.0` | :mark:`N` | :mark:`N` | :mark:`N` | :mark:`N` |
+-----------------------------------+----------------+--------------+------------+------------+------------+
+| `vrrpd` (VRRP) | :mark:`≥5.1` | :mark:`N` | :mark:`N` | :mark:`N` | :mark:`N` |
++-----------------------------------+----------------+--------------+------------+------------+------------+
The indicators have the following semantics:
diff --git a/doc/user/vrrp.rst b/doc/user/vrrp.rst
index 736d9e69a5..435580131b 100644
--- a/doc/user/vrrp.rst
+++ b/doc/user/vrrp.rst
@@ -113,8 +113,6 @@ Suppose you have an interface ``eth0`` with the following configuration:
link/ether 02:17:45:00:aa:aa brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic eth0
valid_lft 72532sec preferred_lft 72532sec
- inet 10.0.2.16/24 brd 10.0.2.255 scope global dynamic eth0
- valid_lft 72532sec preferred_lft 72532sec
inet6 fe80::17:45ff:fe00:aaaa/64 scope link
valid_lft forever preferred_lft forever
@@ -172,6 +170,8 @@ Using ``vrrp4-2-1`` as an example, a few things to note about this interface:
egress via ``eth0``
- Its MAC address is set to the VRRP IPv4 virtual MAC specified by the RFC for
:abbr:`VRID (Virtual Router ID)` ``5``
+- The :abbr:`VIP (Virtual IP)` address ``10.0.2.16`` must not be present on
+ the parent interface ``eth0``.
- The link local address on the interface is not derived from the interface
MAC
diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst
index af465f6fd4..2099dfdd62 100644
--- a/doc/user/zebra.rst
+++ b/doc/user/zebra.rst
@@ -839,11 +839,22 @@ zebra Terminal Mode Commands
.. index:: show ipv6 route
.. clicmd:: show ipv6 route
-.. index:: show interface [{vrf VRF|brief}]
-.. clicmd:: show interface [{vrf VRF|brief}]
+.. index:: show [ip|ipv6] route [PREFIX] [nexthop-group]
+.. clicmd:: show [ip|ipv6] route [PREFIX] [nexthop-group]
-.. index:: show interface [{vrf all|brief}]
-.. clicmd:: show interface [{vrf all|brief}]
+ Display detailed information about a route. If [nexthop-group] is
+ included, it will display the nexthop group ID the route is using as well.
+
+.. index:: show interface [NAME] [{vrf VRF|brief}] [nexthop-group]
+.. clicmd:: show interface [NAME] [{vrf VRF|brief}] [nexthop-group]
+
+.. index:: show interface [NAME] [{vrf all|brief}] [nexthop-group]
+.. clicmd:: show interface [NAME] [{vrf all|brief}] [nexthop-group]
+
+ Display interface information. If no extra information is added, it will
+ dump information on all interfaces. If [NAME] is specified, it will display
+ detailed information about that single interface. If [nexthop-group] is
+ specified, it will display nexthop groups pointing out that interface.
.. index:: show ip prefix-list [NAME]
.. clicmd:: show ip prefix-list [NAME]
@@ -900,3 +911,8 @@ zebra Terminal Mode Commands
Reset statistics related to the zebra code that interacts with the
optional Forwarding Plane Manager (FPM) component.
+.. index:: show nexthop-group [ID] [vrf NAME] [ip|ipv6]
+.. clicmd:: show nexthop-group [ID] [vrf NAME] [ip|ipv6]
+
+ Display nexthop groups created by zebra.
+
diff --git a/eigrpd/eigrp_network.c b/eigrpd/eigrp_network.c
index 3e09ec41bb..324f309290 100644
--- a/eigrpd/eigrp_network.c
+++ b/eigrpd/eigrp_network.c
@@ -327,8 +327,7 @@ int eigrp_network_unset(struct eigrp *eigrp, struct prefix *p)
if (!IPV4_ADDR_SAME(&pref->u.prefix4, &p->u.prefix4))
return 0;
- prefix_ipv4_free(rn->info);
- rn->info = NULL;
+ prefix_ipv4_free((struct prefix_ipv4 **)&rn->info);
route_unlock_node(rn); /* initial reference */
/* Find interfaces that not configured already. */
diff --git a/eigrpd/eigrp_topology.c b/eigrpd/eigrp_topology.c
index 9cc612eaf1..56dbe3d85e 100644
--- a/eigrpd/eigrp_topology.c
+++ b/eigrpd/eigrp_topology.c
@@ -197,7 +197,7 @@ void eigrp_prefix_entry_delete(struct eigrp *eigrp, struct route_table *table,
list_delete(&pe->entries);
list_delete(&pe->rij);
eigrp_zebra_route_delete(eigrp, pe->destination);
- prefix_free(pe->destination);
+ prefix_free(&pe->destination);
rn->info = NULL;
route_unlock_node(rn); // Lookup above
diff --git a/eigrpd/eigrp_zebra.c b/eigrpd/eigrp_zebra.c
index 9a0fdda0f9..3205f13922 100644
--- a/eigrpd/eigrp_zebra.c
+++ b/eigrpd/eigrp_zebra.c
@@ -189,7 +189,7 @@ static int eigrp_interface_address_delete(ZAPI_CALLBACK_ARGS)
if (prefix_cmp(&ei->address, c->address) == 0)
eigrp_if_free(ei, INTERFACE_DOWN_BY_ZEBRA);
- connected_free(c);
+ connected_free(&c);
return 0;
}
diff --git a/include/linux/nexthop.h b/include/linux/nexthop.h
new file mode 100644
index 0000000000..e4d6e256ef
--- /dev/null
+++ b/include/linux/nexthop.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _LINUX_NEXTHOP_H
+#define _LINUX_NEXTHOP_H
+
+#include <linux/types.h>
+
+#define RTM_NHA(h) ((struct rtattr *)(((char *)(h)) + \
+ NLMSG_ALIGN(sizeof(struct nhmsg))))
+
+struct nhmsg {
+ unsigned char nh_family;
+ unsigned char nh_scope; /* return only */
+ unsigned char nh_protocol; /* Routing protocol that installed nh */
+ unsigned char resvd;
+ unsigned int nh_flags; /* RTNH_F flags */
+};
+
+struct nexthop_grp {
+ __u32 id; /* nexthop id - must exist */
+ __u8 weight; /* weight of this nexthop */
+ __u8 resvd1;
+ __u16 resvd2;
+};
+
+enum {
+ NEXTHOP_GRP_TYPE_MPATH, /* default type if not specified */
+ __NEXTHOP_GRP_TYPE_MAX,
+};
+
+#define NEXTHOP_GRP_TYPE_MAX (__NEXTHOP_GRP_TYPE_MAX - 1)
+
+enum {
+ NHA_UNSPEC,
+ NHA_ID, /* u32; id for nexthop. id == 0 means auto-assign */
+
+ NHA_GROUP, /* array of nexthop_grp */
+ NHA_GROUP_TYPE, /* u16 one of NEXTHOP_GRP_TYPE */
+ /* if NHA_GROUP attribute is added, no other attributes can be set */
+
+ NHA_BLACKHOLE, /* flag; nexthop used to blackhole packets */
+ /* if NHA_BLACKHOLE is added, OIF, GATEWAY, ENCAP can not be set */
+
+ NHA_OIF, /* u32; nexthop device */
+ NHA_GATEWAY, /* be32 (IPv4) or in6_addr (IPv6) gw address */
+ NHA_ENCAP_TYPE, /* u16; lwt encap type */
+ NHA_ENCAP, /* lwt encap data */
+
+ /* NHA_OIF can be appended to dump request to return only
+ * nexthops using given device
+ */
+ NHA_GROUPS, /* flag; only return nexthop groups in dump */
+ NHA_MASTER, /* u32; only return nexthops with given master dev */
+
+ __NHA_MAX,
+};
+
+#define NHA_MAX (__NHA_MAX - 1)
+#endif
diff --git a/include/subdir.am b/include/subdir.am
index 0d7fed2852..b1ca1be54f 100644
--- a/include/subdir.am
+++ b/include/subdir.am
@@ -6,6 +6,7 @@ noinst_HEADERS += \
include/linux/mpls_iptunnel.h \
include/linux/neighbour.h \
include/linux/netlink.h \
+ include/linux/nexthop.h \
include/linux/rtnetlink.h \
include/linux/socket.h \
include/linux/net_namespace.h \
diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c
index d2ec6ff566..1d70521e68 100644
--- a/isisd/isis_adjacency.c
+++ b/isisd/isis_adjacency.c
@@ -48,6 +48,7 @@
#include "isisd/isis_mt.h"
#include "isisd/isis_tlvs.h"
#include "isisd/fabricd.h"
+#include "isisd/isis_nb.h"
extern struct isis *isis;
@@ -375,6 +376,20 @@ void isis_adj_print(struct isis_adjacency *adj)
return;
}
+const char *isis_adj_yang_state(enum isis_adj_state state)
+{
+ switch (state) {
+ case ISIS_ADJ_DOWN:
+ return "down";
+ case ISIS_ADJ_UP:
+ return "up";
+ case ISIS_ADJ_INITIALIZING:
+ return "init";
+ default:
+ return "failed";
+ }
+}
+
int isis_adj_expire(struct thread *thread)
{
struct isis_adjacency *adj;
diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h
index 2a4cb6e614..93583fc122 100644
--- a/isisd/isis_adjacency.h
+++ b/isisd/isis_adjacency.h
@@ -121,6 +121,7 @@ DECLARE_HOOK(isis_adj_state_change_hook, (struct isis_adjacency *adj), (adj))
void isis_adj_state_change(struct isis_adjacency *adj,
enum isis_adj_state state, const char *reason);
void isis_adj_print(struct isis_adjacency *adj);
+const char *isis_adj_yang_state(enum isis_adj_state state);
int isis_adj_expire(struct thread *thread);
void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty,
char detail);
diff --git a/isisd/isis_bfd.c b/isisd/isis_bfd.c
index 8fc7997d79..cf4b841798 100644
--- a/isisd/isis_bfd.c
+++ b/isisd/isis_bfd.c
@@ -118,7 +118,8 @@ static void bfd_adj_event(struct isis_adjacency *adj, struct prefix *dst,
int old_status = adj->bfd_session->status;
- adj->bfd_session->status = new_status;
+ BFD_SET_CLIENT_STATUS(adj->bfd_session->status, new_status);
+
if (old_status == new_status)
return;
diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c
index 29fb725b04..8343f7d85f 100644
--- a/isisd/isis_circuit.c
+++ b/isisd/isis_circuit.c
@@ -58,6 +58,7 @@
#include "isisd/isis_mt.h"
#include "isisd/isis_errors.h"
#include "isisd/isis_tx_queue.h"
+#include "isisd/isis_nb.h"
DEFINE_QOBJ_TYPE(isis_circuit)
@@ -337,7 +338,7 @@ void isis_circuit_del_addr(struct isis_circuit *circuit,
if (ip) {
listnode_delete(circuit->ip_addrs, ip);
- prefix_ipv4_free(ip);
+ prefix_ipv4_free(&ip);
if (circuit->area)
lsp_regenerate_schedule(circuit->area,
circuit->is_type, 0);
@@ -357,7 +358,7 @@ void isis_circuit_del_addr(struct isis_circuit *circuit,
zlog_warn("End of addresses");
}
- prefix_ipv4_free(ipv4);
+ prefix_ipv4_free(&ipv4);
}
if (connected->address->family == AF_INET6) {
ipv6 = prefix_ipv6_new();
@@ -373,7 +374,7 @@ void isis_circuit_del_addr(struct isis_circuit *circuit,
}
if (ip6) {
listnode_delete(circuit->ipv6_link, ip6);
- prefix_ipv6_free(ip6);
+ prefix_ipv6_free(&ip6);
found = 1;
}
} else {
@@ -385,7 +386,7 @@ void isis_circuit_del_addr(struct isis_circuit *circuit,
}
if (ip6) {
listnode_delete(circuit->ipv6_non_link, ip6);
- prefix_ipv6_free(ip6);
+ prefix_ipv6_free(&ip6);
found = 1;
}
}
@@ -416,7 +417,7 @@ void isis_circuit_del_addr(struct isis_circuit *circuit,
lsp_regenerate_schedule(circuit->area, circuit->is_type,
0);
- prefix_ipv6_free(ipv6);
+ prefix_ipv6_free(&ipv6);
}
return;
}
diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c
index 37f4dcfabd..3144b3c28e 100644
--- a/isisd/isis_cli.c
+++ b/isisd/isis_cli.c
@@ -32,7 +32,7 @@
#include "yang.h"
#include "lib/linklist.h"
#include "isisd/isisd.h"
-#include "isisd/isis_cli.h"
+#include "isisd/isis_nb.h"
#include "isisd/isis_misc.h"
#include "isisd/isis_circuit.h"
#include "isisd/isis_csm.h"
diff --git a/isisd/isis_cli.h b/isisd/isis_cli.h
deleted file mode 100644
index 6621dc0fc0..0000000000
--- a/isisd/isis_cli.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2018 Volta Networks
- * Emanuele Di Pascale
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; see the file COPYING; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef ISISD_ISIS_CLI_H_
-#define ISISD_ISIS_CLI_H_
-
-/* add cli_show declarations here as externs */
-void cli_show_router_isis(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_ipv4(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_ipv6(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_bfd_monitoring(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_area_address(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_is_type(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_dynamic_hostname(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_attached(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_overload(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_metric_style(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_area_pwd(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_domain_pwd(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_lsp_gen_interval(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_lsp_ref_interval(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_lsp_max_lifetime(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_lsp_mtu(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_spf_min_interval(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_spf_ietf_backoff(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_purge_origin(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_mpls_te(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_mpls_te_router_addr(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_def_origin_ipv4(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_def_origin_ipv6(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_redistribute_ipv4(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_redistribute_ipv6(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_mt_ipv4_multicast(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_mt_ipv4_mgmt(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_mt_ipv6_unicast(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_mt_ipv6_multicast(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_mt_ipv6_mgmt(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_mt_ipv6_dstsrc(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_passive(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_password(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_metric(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_hello_interval(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_hello_multi(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_threeway_shake(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_hello_padding(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_csnp_interval(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_psnp_interval(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_mt_ipv4_unicast(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_mt_ipv4_multicast(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_mt_ipv4_mgmt(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_mt_ipv6_unicast(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_mt_ipv6_multicast(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_mt_ipv6_mgmt(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_mt_ipv6_dstsrc(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_circ_type(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_network_type(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_ip_isis_priority(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-void cli_show_isis_log_adjacency(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-
-#endif /* ISISD_ISIS_CLI_H_ */
diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c
index 061e758831..c0b90e8439 100644
--- a/isisd/isis_lsp.c
+++ b/isisd/isis_lsp.c
@@ -57,6 +57,7 @@
#include "isisd/isis_te.h"
#include "isisd/fabricd.h"
#include "isisd/isis_tx_queue.h"
+#include "isisd/isis_nb.h"
static int lsp_refresh(struct thread *thread);
static int lsp_l1_refresh_pseudo(struct thread *thread);
diff --git a/isisd/isis_main.c b/isisd/isis_main.c
index 718924daf2..2ef0065180 100644
--- a/isisd/isis_main.c
+++ b/isisd/isis_main.c
@@ -57,6 +57,7 @@
#include "isisd/isis_lsp.h"
#include "isisd/isis_mt.h"
#include "isisd/fabricd.h"
+#include "isisd/isis_nb.h"
/* Default configuration file name */
#define ISISD_DEFAULT_CONFIG "isisd.conf"
diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c
new file mode 100644
index 0000000000..d84e533240
--- /dev/null
+++ b/isisd/isis_nb.c
@@ -0,0 +1,815 @@
+/*
+ * Copyright (C) 2018 Volta Networks
+ * Emanuele Di Pascale
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "northbound.h"
+#include "libfrr.h"
+
+#include "isisd/isis_nb.h"
+
+/* clang-format off */
+const struct frr_yang_module_info frr_isisd_info = {
+ .name = "frr-isisd",
+ .nodes = {
+ {
+ .xpath = "/frr-isisd:isis/instance",
+ .cbs = {
+ .cli_show = cli_show_router_isis,
+ .create = isis_instance_create,
+ .destroy = isis_instance_destroy,
+ },
+ .priority = NB_DFLT_PRIORITY - 1,
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/is-type",
+ .cbs = {
+ .cli_show = cli_show_isis_is_type,
+ .modify = isis_instance_is_type_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/area-address",
+ .cbs = {
+ .cli_show = cli_show_isis_area_address,
+ .create = isis_instance_area_address_create,
+ .destroy = isis_instance_area_address_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/dynamic-hostname",
+ .cbs = {
+ .cli_show = cli_show_isis_dynamic_hostname,
+ .modify = isis_instance_dynamic_hostname_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/attached",
+ .cbs = {
+ .cli_show = cli_show_isis_attached,
+ .modify = isis_instance_attached_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/overload",
+ .cbs = {
+ .cli_show = cli_show_isis_overload,
+ .modify = isis_instance_overload_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/metric-style",
+ .cbs = {
+ .cli_show = cli_show_isis_metric_style,
+ .modify = isis_instance_metric_style_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/purge-originator",
+ .cbs = {
+ .cli_show = cli_show_isis_purge_origin,
+ .modify = isis_instance_purge_originator_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/lsp/mtu",
+ .cbs = {
+ .cli_show = cli_show_isis_lsp_mtu,
+ .modify = isis_instance_lsp_mtu_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/lsp/refresh-interval",
+ .cbs = {
+ .cli_show = cli_show_isis_lsp_ref_interval,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/lsp/refresh-interval/level-1",
+ .cbs = {
+ .modify = isis_instance_lsp_refresh_interval_level_1_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/lsp/refresh-interval/level-2",
+ .cbs = {
+ .modify = isis_instance_lsp_refresh_interval_level_2_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/lsp/maximum-lifetime",
+ .cbs = {
+ .cli_show = cli_show_isis_lsp_max_lifetime,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/lsp/maximum-lifetime/level-1",
+ .cbs = {
+ .modify = isis_instance_lsp_maximum_lifetime_level_1_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/lsp/maximum-lifetime/level-2",
+ .cbs = {
+ .modify = isis_instance_lsp_maximum_lifetime_level_2_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/lsp/generation-interval",
+ .cbs = {
+ .cli_show = cli_show_isis_lsp_gen_interval,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/lsp/generation-interval/level-1",
+ .cbs = {
+ .modify = isis_instance_lsp_generation_interval_level_1_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/lsp/generation-interval/level-2",
+ .cbs = {
+ .modify = isis_instance_lsp_generation_interval_level_2_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay",
+ .cbs = {
+ .apply_finish = ietf_backoff_delay_apply_finish,
+ .cli_show = cli_show_isis_spf_ietf_backoff,
+ .create = isis_instance_spf_ietf_backoff_delay_create,
+ .destroy = isis_instance_spf_ietf_backoff_delay_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay/init-delay",
+ .cbs = {
+ .modify = isis_instance_spf_ietf_backoff_delay_init_delay_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay/short-delay",
+ .cbs = {
+ .modify = isis_instance_spf_ietf_backoff_delay_short_delay_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay/long-delay",
+ .cbs = {
+ .modify = isis_instance_spf_ietf_backoff_delay_long_delay_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay/hold-down",
+ .cbs = {
+ .modify = isis_instance_spf_ietf_backoff_delay_hold_down_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay/time-to-learn",
+ .cbs = {
+ .modify = isis_instance_spf_ietf_backoff_delay_time_to_learn_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/spf/minimum-interval",
+ .cbs = {
+ .cli_show = cli_show_isis_spf_min_interval,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/spf/minimum-interval/level-1",
+ .cbs = {
+ .modify = isis_instance_spf_minimum_interval_level_1_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/spf/minimum-interval/level-2",
+ .cbs = {
+ .modify = isis_instance_spf_minimum_interval_level_2_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/area-password",
+ .cbs = {
+ .apply_finish = area_password_apply_finish,
+ .cli_show = cli_show_isis_area_pwd,
+ .create = isis_instance_area_password_create,
+ .destroy = isis_instance_area_password_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/area-password/password",
+ .cbs = {
+ .modify = isis_instance_area_password_password_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/area-password/password-type",
+ .cbs = {
+ .modify = isis_instance_area_password_password_type_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/area-password/authenticate-snp",
+ .cbs = {
+ .modify = isis_instance_area_password_authenticate_snp_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/domain-password",
+ .cbs = {
+ .apply_finish = domain_password_apply_finish,
+ .cli_show = cli_show_isis_domain_pwd,
+ .create = isis_instance_domain_password_create,
+ .destroy = isis_instance_domain_password_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/domain-password/password",
+ .cbs = {
+ .modify = isis_instance_domain_password_password_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/domain-password/password-type",
+ .cbs = {
+ .modify = isis_instance_domain_password_password_type_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/domain-password/authenticate-snp",
+ .cbs = {
+ .modify = isis_instance_domain_password_authenticate_snp_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv4",
+ .cbs = {
+ .apply_finish = default_info_origin_ipv4_apply_finish,
+ .cli_show = cli_show_isis_def_origin_ipv4,
+ .create = isis_instance_default_information_originate_ipv4_create,
+ .destroy = isis_instance_default_information_originate_ipv4_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv4/always",
+ .cbs = {
+ .modify = isis_instance_default_information_originate_ipv4_always_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv4/route-map",
+ .cbs = {
+ .destroy = isis_instance_default_information_originate_ipv4_route_map_destroy,
+ .modify = isis_instance_default_information_originate_ipv4_route_map_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv4/metric",
+ .cbs = {
+ .modify = isis_instance_default_information_originate_ipv4_metric_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv6",
+ .cbs = {
+ .apply_finish = default_info_origin_ipv6_apply_finish,
+ .cli_show = cli_show_isis_def_origin_ipv6,
+ .create = isis_instance_default_information_originate_ipv6_create,
+ .destroy = isis_instance_default_information_originate_ipv6_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv6/always",
+ .cbs = {
+ .modify = isis_instance_default_information_originate_ipv6_always_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv6/route-map",
+ .cbs = {
+ .destroy = isis_instance_default_information_originate_ipv6_route_map_destroy,
+ .modify = isis_instance_default_information_originate_ipv6_route_map_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv6/metric",
+ .cbs = {
+ .modify = isis_instance_default_information_originate_ipv6_metric_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/redistribute/ipv4",
+ .cbs = {
+ .apply_finish = redistribute_ipv4_apply_finish,
+ .cli_show = cli_show_isis_redistribute_ipv4,
+ .create = isis_instance_redistribute_ipv4_create,
+ .destroy = isis_instance_redistribute_ipv4_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/redistribute/ipv4/route-map",
+ .cbs = {
+ .destroy = isis_instance_redistribute_ipv4_route_map_destroy,
+ .modify = isis_instance_redistribute_ipv4_route_map_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/redistribute/ipv4/metric",
+ .cbs = {
+ .modify = isis_instance_redistribute_ipv4_metric_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/redistribute/ipv6",
+ .cbs = {
+ .apply_finish = redistribute_ipv6_apply_finish,
+ .cli_show = cli_show_isis_redistribute_ipv6,
+ .create = isis_instance_redistribute_ipv6_create,
+ .destroy = isis_instance_redistribute_ipv6_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/redistribute/ipv6/route-map",
+ .cbs = {
+ .destroy = isis_instance_redistribute_ipv6_route_map_destroy,
+ .modify = isis_instance_redistribute_ipv6_route_map_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/redistribute/ipv6/metric",
+ .cbs = {
+ .modify = isis_instance_redistribute_ipv6_metric_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/multi-topology/ipv4-multicast",
+ .cbs = {
+ .cli_show = cli_show_isis_mt_ipv4_multicast,
+ .create = isis_instance_multi_topology_ipv4_multicast_create,
+ .destroy = isis_instance_multi_topology_ipv4_multicast_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/multi-topology/ipv4-multicast/overload",
+ .cbs = {
+ .modify = isis_instance_multi_topology_ipv4_multicast_overload_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/multi-topology/ipv4-management",
+ .cbs = {
+ .cli_show = cli_show_isis_mt_ipv4_mgmt,
+ .create = isis_instance_multi_topology_ipv4_management_create,
+ .destroy = isis_instance_multi_topology_ipv4_management_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/multi-topology/ipv4-management/overload",
+ .cbs = {
+ .modify = isis_instance_multi_topology_ipv4_management_overload_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-unicast",
+ .cbs = {
+ .cli_show = cli_show_isis_mt_ipv6_unicast,
+ .create = isis_instance_multi_topology_ipv6_unicast_create,
+ .destroy = isis_instance_multi_topology_ipv6_unicast_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-unicast/overload",
+ .cbs = {
+ .modify = isis_instance_multi_topology_ipv6_unicast_overload_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-multicast",
+ .cbs = {
+ .cli_show = cli_show_isis_mt_ipv6_multicast,
+ .create = isis_instance_multi_topology_ipv6_multicast_create,
+ .destroy = isis_instance_multi_topology_ipv6_multicast_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-multicast/overload",
+ .cbs = {
+ .modify = isis_instance_multi_topology_ipv6_multicast_overload_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-management",
+ .cbs = {
+ .cli_show = cli_show_isis_mt_ipv6_mgmt,
+ .create = isis_instance_multi_topology_ipv6_management_create,
+ .destroy = isis_instance_multi_topology_ipv6_management_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-management/overload",
+ .cbs = {
+ .modify = isis_instance_multi_topology_ipv6_management_overload_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-dstsrc",
+ .cbs = {
+ .cli_show = cli_show_isis_mt_ipv6_dstsrc,
+ .create = isis_instance_multi_topology_ipv6_dstsrc_create,
+ .destroy = isis_instance_multi_topology_ipv6_dstsrc_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-dstsrc/overload",
+ .cbs = {
+ .modify = isis_instance_multi_topology_ipv6_dstsrc_overload_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/log-adjacency-changes",
+ .cbs = {
+ .cli_show = cli_show_isis_log_adjacency,
+ .modify = isis_instance_log_adjacency_changes_modify,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/mpls-te",
+ .cbs = {
+ .cli_show = cli_show_isis_mpls_te,
+ .create = isis_instance_mpls_te_create,
+ .destroy = isis_instance_mpls_te_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/mpls-te/router-address",
+ .cbs = {
+ .cli_show = cli_show_isis_mpls_te_router_addr,
+ .destroy = isis_instance_mpls_te_router_address_destroy,
+ .modify = isis_instance_mpls_te_router_address_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis",
+ .cbs = {
+ .create = lib_interface_isis_create,
+ .destroy = lib_interface_isis_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/area-tag",
+ .cbs = {
+ .modify = lib_interface_isis_area_tag_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/circuit-type",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_circ_type,
+ .modify = lib_interface_isis_circuit_type_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/ipv4-routing",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_ipv4,
+ .modify = lib_interface_isis_ipv4_routing_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/ipv6-routing",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_ipv6,
+ .modify = lib_interface_isis_ipv6_routing_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring",
+ .cbs = {
+ .modify = lib_interface_isis_bfd_monitoring_modify,
+ .cli_show = cli_show_ip_isis_bfd_monitoring,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/csnp-interval",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_csnp_interval,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/csnp-interval/level-1",
+ .cbs = {
+ .modify = lib_interface_isis_csnp_interval_level_1_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/csnp-interval/level-2",
+ .cbs = {
+ .modify = lib_interface_isis_csnp_interval_level_2_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/psnp-interval",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_psnp_interval,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/psnp-interval/level-1",
+ .cbs = {
+ .modify = lib_interface_isis_psnp_interval_level_1_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/psnp-interval/level-2",
+ .cbs = {
+ .modify = lib_interface_isis_psnp_interval_level_2_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/padding",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_hello_padding,
+ .modify = lib_interface_isis_hello_padding_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/interval",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_hello_interval,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-1",
+ .cbs = {
+ .modify = lib_interface_isis_hello_interval_level_1_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-2",
+ .cbs = {
+ .modify = lib_interface_isis_hello_interval_level_2_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/multiplier",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_hello_multi,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/multiplier/level-1",
+ .cbs = {
+ .modify = lib_interface_isis_hello_multiplier_level_1_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/multiplier/level-2",
+ .cbs = {
+ .modify = lib_interface_isis_hello_multiplier_level_2_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/metric",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_metric,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/metric/level-1",
+ .cbs = {
+ .modify = lib_interface_isis_metric_level_1_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/metric/level-2",
+ .cbs = {
+ .modify = lib_interface_isis_metric_level_2_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/priority",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_priority,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/priority/level-1",
+ .cbs = {
+ .modify = lib_interface_isis_priority_level_1_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/priority/level-2",
+ .cbs = {
+ .modify = lib_interface_isis_priority_level_2_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/network-type",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_network_type,
+ .modify = lib_interface_isis_network_type_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/passive",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_passive,
+ .modify = lib_interface_isis_passive_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/password",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_password,
+ .create = lib_interface_isis_password_create,
+ .destroy = lib_interface_isis_password_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/password/password",
+ .cbs = {
+ .modify = lib_interface_isis_password_password_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/password/password-type",
+ .cbs = {
+ .modify = lib_interface_isis_password_password_type_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/disable-three-way-handshake",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_threeway_shake,
+ .modify = lib_interface_isis_disable_three_way_handshake_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-unicast",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_mt_ipv4_unicast,
+ .modify = lib_interface_isis_multi_topology_ipv4_unicast_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-multicast",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_mt_ipv4_multicast,
+ .modify = lib_interface_isis_multi_topology_ipv4_multicast_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-management",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_mt_ipv4_mgmt,
+ .modify = lib_interface_isis_multi_topology_ipv4_management_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-unicast",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_mt_ipv6_unicast,
+ .modify = lib_interface_isis_multi_topology_ipv6_unicast_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-multicast",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_mt_ipv6_multicast,
+ .modify = lib_interface_isis_multi_topology_ipv6_multicast_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-management",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_mt_ipv6_mgmt,
+ .modify = lib_interface_isis_multi_topology_ipv6_management_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-dstsrc",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_mt_ipv6_dstsrc,
+ .modify = lib_interface_isis_multi_topology_ipv6_dstsrc_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency",
+ .cbs = {
+ .get_next = lib_interface_isis_adjacencies_adjacency_get_next,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/neighbor-sys-type",
+ .cbs = {
+ .get_elem = lib_interface_isis_adjacencies_adjacency_neighbor_sys_type_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/neighbor-sysid",
+ .cbs = {
+ .get_elem = lib_interface_isis_adjacencies_adjacency_neighbor_sysid_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/neighbor-extended-circuit-id",
+ .cbs = {
+ .get_elem = lib_interface_isis_adjacencies_adjacency_neighbor_extended_circuit_id_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/neighbor-snpa",
+ .cbs = {
+ .get_elem = lib_interface_isis_adjacencies_adjacency_neighbor_snpa_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/hold-timer",
+ .cbs = {
+ .get_elem = lib_interface_isis_adjacencies_adjacency_hold_timer_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/neighbor-priority",
+ .cbs = {
+ .get_elem = lib_interface_isis_adjacencies_adjacency_neighbor_priority_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/state",
+ .cbs = {
+ .get_elem = lib_interface_isis_adjacencies_adjacency_state_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/event-counters/adjacency-changes",
+ .cbs = {
+ .get_elem = lib_interface_isis_event_counters_adjacency_changes_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/event-counters/adjacency-number",
+ .cbs = {
+ .get_elem = lib_interface_isis_event_counters_adjacency_number_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/event-counters/init-fails",
+ .cbs = {
+ .get_elem = lib_interface_isis_event_counters_init_fails_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/event-counters/adjacency-rejects",
+ .cbs = {
+ .get_elem = lib_interface_isis_event_counters_adjacency_rejects_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/event-counters/id-len-mismatch",
+ .cbs = {
+ .get_elem = lib_interface_isis_event_counters_id_len_mismatch_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/event-counters/max-area-addresses-mismatch",
+ .cbs = {
+ .get_elem = lib_interface_isis_event_counters_max_area_addresses_mismatch_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/event-counters/authentication-type-fails",
+ .cbs = {
+ .get_elem = lib_interface_isis_event_counters_authentication_type_fails_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/event-counters/authentication-fails",
+ .cbs = {
+ .get_elem = lib_interface_isis_event_counters_authentication_fails_get_elem,
+ }
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
diff --git a/isisd/isis_nb.h b/isisd/isis_nb.h
new file mode 100644
index 0000000000..29a2ded0de
--- /dev/null
+++ b/isisd/isis_nb.h
@@ -0,0 +1,548 @@
+/*
+ * Copyright (C) 2018 Volta Networks
+ * Emanuele Di Pascale
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef ISISD_ISIS_NB_H_
+#define ISISD_ISIS_NB_H_
+
+extern const struct frr_yang_module_info frr_isisd_info;
+
+/* Forward declaration(s). */
+struct isis_area;
+struct isis_circuit;
+struct isis_adjacency;
+
+/* Mandatory callbacks. */
+int isis_instance_create(enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_destroy(enum nb_event event, const struct lyd_node *dnode);
+int isis_instance_is_type_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_area_address_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_area_address_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int isis_instance_dynamic_hostname_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_attached_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_overload_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_metric_style_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_purge_originator_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_lsp_mtu_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_lsp_refresh_interval_level_1_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_lsp_refresh_interval_level_2_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_lsp_maximum_lifetime_level_1_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_lsp_maximum_lifetime_level_2_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_lsp_generation_interval_level_1_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_lsp_generation_interval_level_2_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_spf_ietf_backoff_delay_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_spf_ietf_backoff_delay_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int isis_instance_spf_ietf_backoff_delay_init_delay_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_spf_ietf_backoff_delay_short_delay_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_spf_ietf_backoff_delay_long_delay_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_spf_ietf_backoff_delay_hold_down_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_spf_ietf_backoff_delay_time_to_learn_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_spf_minimum_interval_level_1_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_spf_minimum_interval_level_2_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_area_password_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_area_password_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int isis_instance_area_password_password_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_area_password_password_type_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_area_password_authenticate_snp_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_domain_password_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_domain_password_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int isis_instance_domain_password_password_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_domain_password_password_type_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_domain_password_authenticate_snp_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_default_information_originate_ipv4_create(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_default_information_originate_ipv4_destroy(
+ enum nb_event event, const struct lyd_node *dnode);
+int isis_instance_default_information_originate_ipv4_always_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_default_information_originate_ipv4_route_map_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_default_information_originate_ipv4_route_map_destroy(
+ enum nb_event event, const struct lyd_node *dnode);
+int isis_instance_default_information_originate_ipv4_metric_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_default_information_originate_ipv6_create(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_default_information_originate_ipv6_destroy(
+ enum nb_event event, const struct lyd_node *dnode);
+int isis_instance_default_information_originate_ipv6_always_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_default_information_originate_ipv6_route_map_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_default_information_originate_ipv6_route_map_destroy(
+ enum nb_event event, const struct lyd_node *dnode);
+int isis_instance_default_information_originate_ipv6_metric_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_redistribute_ipv4_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_redistribute_ipv4_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int isis_instance_redistribute_ipv4_route_map_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_redistribute_ipv4_route_map_destroy(
+ enum nb_event event, const struct lyd_node *dnode);
+int isis_instance_redistribute_ipv4_metric_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_redistribute_ipv6_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_redistribute_ipv6_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int isis_instance_redistribute_ipv6_route_map_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_redistribute_ipv6_route_map_destroy(
+ enum nb_event event, const struct lyd_node *dnode);
+int isis_instance_redistribute_ipv6_metric_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_multi_topology_ipv4_multicast_create(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_multi_topology_ipv4_multicast_destroy(
+ enum nb_event event, const struct lyd_node *dnode);
+int isis_instance_multi_topology_ipv4_multicast_overload_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_multi_topology_ipv4_management_create(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_multi_topology_ipv4_management_destroy(
+ enum nb_event event, const struct lyd_node *dnode);
+int isis_instance_multi_topology_ipv4_management_overload_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_multi_topology_ipv6_unicast_create(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_multi_topology_ipv6_unicast_destroy(
+ enum nb_event event, const struct lyd_node *dnode);
+int isis_instance_multi_topology_ipv6_unicast_overload_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_multi_topology_ipv6_multicast_create(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_multi_topology_ipv6_multicast_destroy(
+ enum nb_event event, const struct lyd_node *dnode);
+int isis_instance_multi_topology_ipv6_multicast_overload_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_multi_topology_ipv6_management_create(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_multi_topology_ipv6_management_destroy(
+ enum nb_event event, const struct lyd_node *dnode);
+int isis_instance_multi_topology_ipv6_management_overload_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_multi_topology_ipv6_dstsrc_create(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_multi_topology_ipv6_dstsrc_destroy(
+ enum nb_event event, const struct lyd_node *dnode);
+int isis_instance_multi_topology_ipv6_dstsrc_overload_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_log_adjacency_changes_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_mpls_te_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_mpls_te_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int isis_instance_mpls_te_router_address_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int isis_instance_mpls_te_router_address_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int lib_interface_isis_create(enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int lib_interface_isis_area_tag_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_ipv4_routing_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_ipv6_routing_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_circuit_type_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_bfd_monitoring_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_csnp_interval_level_1_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_csnp_interval_level_2_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_psnp_interval_level_1_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_psnp_interval_level_2_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_hello_padding_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_hello_interval_level_1_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_hello_interval_level_2_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_hello_multiplier_level_1_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_hello_multiplier_level_2_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_metric_level_1_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_metric_level_2_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_priority_level_1_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_priority_level_2_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_network_type_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_passive_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_password_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_password_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int lib_interface_isis_password_password_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_password_password_type_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_disable_three_way_handshake_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_multi_topology_ipv4_unicast_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_multi_topology_ipv4_multicast_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_multi_topology_ipv4_management_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_multi_topology_ipv6_unicast_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_multi_topology_ipv6_multicast_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_multi_topology_ipv6_management_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_isis_multi_topology_ipv6_dstsrc_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+const void *
+lib_interface_isis_adjacencies_adjacency_get_next(const void *parent_list_entry,
+ const void *list_entry);
+struct yang_data *
+lib_interface_isis_adjacencies_adjacency_neighbor_sys_type_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+lib_interface_isis_adjacencies_adjacency_neighbor_sysid_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+lib_interface_isis_adjacencies_adjacency_neighbor_extended_circuit_id_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+lib_interface_isis_adjacencies_adjacency_neighbor_snpa_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *lib_interface_isis_adjacencies_adjacency_hold_timer_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+lib_interface_isis_adjacencies_adjacency_neighbor_priority_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+lib_interface_isis_adjacencies_adjacency_state_get_elem(const char *xpath,
+ const void *list_entry);
+struct yang_data *lib_interface_isis_event_counters_adjacency_changes_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *lib_interface_isis_event_counters_adjacency_number_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+lib_interface_isis_event_counters_init_fails_get_elem(const char *xpath,
+ const void *list_entry);
+struct yang_data *lib_interface_isis_event_counters_adjacency_rejects_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *lib_interface_isis_event_counters_id_len_mismatch_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+lib_interface_isis_event_counters_max_area_addresses_mismatch_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+lib_interface_isis_event_counters_authentication_type_fails_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+lib_interface_isis_event_counters_authentication_fails_get_elem(
+ const char *xpath, const void *list_entry);
+
+/* Optional 'apply_finish' callbacks. */
+void ietf_backoff_delay_apply_finish(const struct lyd_node *dnode);
+void area_password_apply_finish(const struct lyd_node *dnode);
+void domain_password_apply_finish(const struct lyd_node *dnode);
+void default_info_origin_apply_finish(const struct lyd_node *dnode, int family);
+void default_info_origin_ipv4_apply_finish(const struct lyd_node *dnode);
+void default_info_origin_ipv6_apply_finish(const struct lyd_node *dnode);
+void redistribute_apply_finish(const struct lyd_node *dnode, int family);
+void redistribute_ipv4_apply_finish(const struct lyd_node *dnode);
+void redistribute_ipv6_apply_finish(const struct lyd_node *dnode);
+
+/* Optional 'cli_show' callbacks. */
+void cli_show_router_isis(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_ipv4(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_ipv6(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_bfd_monitoring(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_area_address(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_is_type(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_dynamic_hostname(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_attached(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_overload(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_metric_style(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_area_pwd(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_domain_pwd(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_lsp_gen_interval(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_lsp_ref_interval(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_lsp_max_lifetime(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_lsp_mtu(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_spf_min_interval(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_spf_ietf_backoff(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_purge_origin(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_mpls_te(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_mpls_te_router_addr(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_def_origin_ipv4(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_def_origin_ipv6(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_redistribute_ipv4(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_redistribute_ipv6(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_mt_ipv4_multicast(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_mt_ipv4_mgmt(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_mt_ipv6_unicast(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_mt_ipv6_multicast(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_mt_ipv6_mgmt(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_mt_ipv6_dstsrc(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_passive(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_password(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_metric(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_hello_interval(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_hello_multi(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_threeway_shake(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_hello_padding(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_csnp_interval(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_psnp_interval(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_mt_ipv4_unicast(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_mt_ipv4_multicast(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_mt_ipv4_mgmt(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_mt_ipv6_unicast(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_mt_ipv6_multicast(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_mt_ipv6_mgmt(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_mt_ipv6_dstsrc(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_circ_type(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_network_type(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_isis_priority(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_log_adjacency(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+
+/* Notifications. */
+void isis_notif_db_overload(const struct isis_area *area, bool overload);
+void isis_notif_lsp_too_large(const struct isis_circuit *circuit,
+ uint32_t pdu_size, const char *lsp_id);
+void isis_notif_if_state_change(const struct isis_circuit *circuit, bool down);
+void isis_notif_corrupted_lsp(const struct isis_area *area,
+ const char *lsp_id); /* currently unused */
+void isis_notif_lsp_exceed_max(const struct isis_area *area,
+ const char *lsp_id);
+void isis_notif_max_area_addr_mismatch(const struct isis_circuit *circuit,
+ uint8_t max_area_addrs,
+ const char *raw_pdu);
+void isis_notif_authentication_type_failure(const struct isis_circuit *circuit,
+ const char *raw_pdu);
+void isis_notif_authentication_failure(const struct isis_circuit *circuit,
+ const char *raw_pdu);
+void isis_notif_adj_state_change(const struct isis_adjacency *adj,
+ int new_state, const char *reason);
+void isis_notif_reject_adjacency(const struct isis_circuit *circuit,
+ const char *reason, const char *raw_pdu);
+void isis_notif_area_mismatch(const struct isis_circuit *circuit,
+ const char *raw_pdu);
+void isis_notif_lsp_received(const struct isis_circuit *circuit,
+ const char *lsp_id, uint32_t seqno,
+ uint32_t timestamp, const char *sys_id);
+void isis_notif_lsp_gen(const struct isis_area *area, const char *lsp_id,
+ uint32_t seqno, uint32_t timestamp);
+void isis_notif_id_len_mismatch(const struct isis_circuit *circuit,
+ uint8_t rcv_id_len, const char *raw_pdu);
+void isis_notif_version_skew(const struct isis_circuit *circuit,
+ uint8_t version, const char *raw_pdu);
+void isis_notif_lsp_error(const struct isis_circuit *circuit,
+ const char *lsp_id, const char *raw_pdu,
+ uint32_t offset, uint8_t tlv_type);
+void isis_notif_seqno_skipped(const struct isis_circuit *circuit,
+ const char *lsp_id);
+void isis_notif_own_lsp_purge(const struct isis_circuit *circuit,
+ const char *lsp_id);
+
+#endif /* ISISD_ISIS_NB_H_ */
diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c
new file mode 100644
index 0000000000..820cfaa426
--- /dev/null
+++ b/isisd/isis_nb_config.c
@@ -0,0 +1,2236 @@
+/*
+ * Copyright (C) 2001,2002 Sampo Saaristo
+ * Tampere University of Technology
+ * Institute of Communications Engineering
+ * Copyright (C) 2018 Volta Networks
+ * Emanuele Di Pascale
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "northbound.h"
+#include "linklist.h"
+#include "log.h"
+#include "bfd.h"
+#include "spf_backoff.h"
+#include "lib_errors.h"
+#include "vrf.h"
+
+#include "isisd/isisd.h"
+#include "isisd/isis_nb.h"
+#include "isisd/isis_common.h"
+#include "isisd/isis_bfd.h"
+#include "isisd/isis_circuit.h"
+#include "isisd/isis_lsp.h"
+#include "isisd/isis_dynhn.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_csm.h"
+#include "isisd/isis_adjacency.h"
+#include "isisd/isis_spf.h"
+#include "isisd/isis_te.h"
+#include "isisd/isis_memory.h"
+#include "isisd/isis_mt.h"
+#include "isisd/isis_redist.h"
+
+/*
+ * XPath: /frr-isisd:isis/instance
+ */
+int isis_instance_create(enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_area *area;
+ const char *area_tag;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area_tag = yang_dnode_get_string(dnode, "./area-tag");
+ area = isis_area_lookup(area_tag);
+ if (area)
+ return NB_ERR_INCONSISTENCY;
+
+ area = isis_area_create(area_tag);
+ /* save area in dnode to avoid looking it up all the time */
+ nb_running_set_entry(dnode, area);
+
+ return NB_OK;
+}
+
+int isis_instance_destroy(enum nb_event event, const struct lyd_node *dnode)
+{
+ struct isis_area *area;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_unset_entry(dnode);
+ isis_area_destroy(area->area_tag);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/is-type
+ */
+int isis_instance_is_type_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_area *area;
+ int type;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ type = yang_dnode_get_enum(dnode, NULL);
+ isis_area_is_type_set(area, type);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/area-address
+ */
+int isis_instance_area_address_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_area *area;
+ struct area_addr addr, *addrr = NULL, *addrp = NULL;
+ struct listnode *node;
+ uint8_t buff[255];
+ const char *net_title = yang_dnode_get_string(dnode, NULL);
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ addr.addr_len = dotformat2buff(buff, net_title);
+ memcpy(addr.area_addr, buff, addr.addr_len);
+ if (addr.area_addr[addr.addr_len - 1] != 0) {
+ flog_warn(
+ EC_LIB_NB_CB_CONFIG_VALIDATE,
+ "nsel byte (last byte) in area address must be 0");
+ return NB_ERR_VALIDATION;
+ }
+ if (isis->sysid_set) {
+ /* Check that the SystemID portions match */
+ if (memcmp(isis->sysid, GETSYSID((&addr)),
+ ISIS_SYS_ID_LEN)) {
+ flog_warn(
+ EC_LIB_NB_CB_CONFIG_VALIDATE,
+ "System ID must not change when defining additional area addresses");
+ return NB_ERR_VALIDATION;
+ }
+ }
+ break;
+ case NB_EV_PREPARE:
+ addrr = XMALLOC(MTYPE_ISIS_AREA_ADDR, sizeof(struct area_addr));
+ addrr->addr_len = dotformat2buff(buff, net_title);
+ memcpy(addrr->area_addr, buff, addrr->addr_len);
+ resource->ptr = addrr;
+ break;
+ case NB_EV_ABORT:
+ XFREE(MTYPE_ISIS_AREA_ADDR, resource->ptr);
+ break;
+ case NB_EV_APPLY:
+ area = nb_running_get_entry(dnode, NULL, true);
+ addrr = resource->ptr;
+
+ if (isis->sysid_set == 0) {
+ /*
+ * First area address - get the SystemID for this router
+ */
+ memcpy(isis->sysid, GETSYSID(addrr), ISIS_SYS_ID_LEN);
+ isis->sysid_set = 1;
+ } else {
+ /* check that we don't already have this address */
+ for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node,
+ addrp)) {
+ if ((addrp->addr_len + ISIS_SYS_ID_LEN
+ + ISIS_NSEL_LEN)
+ != (addrr->addr_len))
+ continue;
+ if (!memcmp(addrp->area_addr, addrr->area_addr,
+ addrr->addr_len)) {
+ XFREE(MTYPE_ISIS_AREA_ADDR, addrr);
+ return NB_OK; /* silent fail */
+ }
+ }
+ }
+
+ /*Forget the systemID part of the address */
+ addrr->addr_len -= (ISIS_SYS_ID_LEN + ISIS_NSEL_LEN);
+ assert(area->area_addrs); /* to silence scan-build sillyness */
+ listnode_add(area->area_addrs, addrr);
+
+ /* only now we can safely generate our LSPs for this area */
+ if (listcount(area->area_addrs) > 0) {
+ if (area->is_type & IS_LEVEL_1)
+ lsp_generate(area, IS_LEVEL_1);
+ if (area->is_type & IS_LEVEL_2)
+ lsp_generate(area, IS_LEVEL_2);
+ }
+ break;
+ }
+
+ return NB_OK;
+}
+
+int isis_instance_area_address_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct area_addr addr, *addrp = NULL;
+ struct listnode *node;
+ uint8_t buff[255];
+ struct isis_area *area;
+ const char *net_title;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ net_title = yang_dnode_get_string(dnode, NULL);
+ addr.addr_len = dotformat2buff(buff, net_title);
+ memcpy(addr.area_addr, buff, (int)addr.addr_len);
+ area = nb_running_get_entry(dnode, NULL, true);
+ for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node, addrp)) {
+ if ((addrp->addr_len + ISIS_SYS_ID_LEN + 1) == addr.addr_len
+ && !memcmp(addrp->area_addr, addr.area_addr, addr.addr_len))
+ break;
+ }
+ if (!addrp)
+ return NB_ERR_INCONSISTENCY;
+
+ listnode_delete(area->area_addrs, addrp);
+ XFREE(MTYPE_ISIS_AREA_ADDR, addrp);
+ /*
+ * Last area address - reset the SystemID for this router
+ */
+ if (listcount(area->area_addrs) == 0) {
+ memset(isis->sysid, 0, ISIS_SYS_ID_LEN);
+ isis->sysid_set = 0;
+ if (isis->debugs & DEBUG_EVENTS)
+ zlog_debug("Router has no SystemID");
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/dynamic-hostname
+ */
+int isis_instance_dynamic_hostname_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_area *area;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ isis_area_dynhostname_set(area, yang_dnode_get_bool(dnode, NULL));
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/attached
+ */
+int isis_instance_attached_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_area *area;
+ bool attached;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ attached = yang_dnode_get_bool(dnode, NULL);
+ isis_area_attached_bit_set(area, attached);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/overload
+ */
+int isis_instance_overload_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_area *area;
+ bool overload;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ overload = yang_dnode_get_bool(dnode, NULL);
+ isis_area_overload_bit_set(area, overload);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/metric-style
+ */
+int isis_instance_metric_style_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_area *area;
+ bool old_metric, new_metric;
+ enum isis_metric_style metric_style = yang_dnode_get_enum(dnode, NULL);
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ old_metric = (metric_style == ISIS_WIDE_METRIC) ? false : true;
+ new_metric = (metric_style == ISIS_NARROW_METRIC) ? false : true;
+ isis_area_metricstyle_set(area, old_metric, new_metric);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/purge-originator
+ */
+int isis_instance_purge_originator_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_area *area;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ area->purge_originator = yang_dnode_get_bool(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/lsp/mtu
+ */
+int isis_instance_lsp_mtu_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct listnode *node;
+ struct isis_circuit *circuit;
+ uint16_t lsp_mtu = yang_dnode_get_uint16(dnode, NULL);
+ struct isis_area *area;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ area = nb_running_get_entry(dnode, NULL, false);
+ if (!area)
+ break;
+ for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
+ if (circuit->state != C_STATE_INIT
+ && circuit->state != C_STATE_UP)
+ continue;
+ if (lsp_mtu > isis_circuit_pdu_size(circuit)) {
+ flog_warn(
+ EC_LIB_NB_CB_CONFIG_VALIDATE,
+ "ISIS area contains circuit %s, which has a maximum PDU size of %zu",
+ circuit->interface->name,
+ isis_circuit_pdu_size(circuit));
+ return NB_ERR_VALIDATION;
+ }
+ }
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ area = nb_running_get_entry(dnode, NULL, true);
+ isis_area_lsp_mtu_set(area, lsp_mtu);
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/lsp/refresh-interval/level-1
+ */
+int isis_instance_lsp_refresh_interval_level_1_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_area *area;
+ uint16_t refr_int;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ refr_int = yang_dnode_get_uint16(dnode, NULL);
+ area = nb_running_get_entry(dnode, NULL, true);
+ isis_area_lsp_refresh_set(area, IS_LEVEL_1, refr_int);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/lsp/refresh-interval/level-2
+ */
+int isis_instance_lsp_refresh_interval_level_2_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_area *area;
+ uint16_t refr_int;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ refr_int = yang_dnode_get_uint16(dnode, NULL);
+ area = nb_running_get_entry(dnode, NULL, true);
+ isis_area_lsp_refresh_set(area, IS_LEVEL_2, refr_int);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/lsp/maximum-lifetime/level-1
+ */
+int isis_instance_lsp_maximum_lifetime_level_1_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_area *area;
+ uint16_t max_lt;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ max_lt = yang_dnode_get_uint16(dnode, NULL);
+ area = nb_running_get_entry(dnode, NULL, true);
+ isis_area_max_lsp_lifetime_set(area, IS_LEVEL_1, max_lt);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/lsp/maximum-lifetime/level-2
+ */
+int isis_instance_lsp_maximum_lifetime_level_2_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_area *area;
+ uint16_t max_lt;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ max_lt = yang_dnode_get_uint16(dnode, NULL);
+ area = nb_running_get_entry(dnode, NULL, true);
+ isis_area_max_lsp_lifetime_set(area, IS_LEVEL_2, max_lt);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/lsp/generation-interval/level-1
+ */
+int isis_instance_lsp_generation_interval_level_1_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_area *area;
+ uint16_t gen_int;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ gen_int = yang_dnode_get_uint16(dnode, NULL);
+ area = nb_running_get_entry(dnode, NULL, true);
+ area->lsp_gen_interval[0] = gen_int;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/lsp/generation-interval/level-2
+ */
+int isis_instance_lsp_generation_interval_level_2_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_area *area;
+ uint16_t gen_int;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ gen_int = yang_dnode_get_uint16(dnode, NULL);
+ area = nb_running_get_entry(dnode, NULL, true);
+ area->lsp_gen_interval[1] = gen_int;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay
+ */
+void ietf_backoff_delay_apply_finish(const struct lyd_node *dnode)
+{
+ long init_delay = yang_dnode_get_uint16(dnode, "./init-delay");
+ long short_delay = yang_dnode_get_uint16(dnode, "./short-delay");
+ long long_delay = yang_dnode_get_uint16(dnode, "./long-delay");
+ long holddown = yang_dnode_get_uint16(dnode, "./hold-down");
+ long timetolearn = yang_dnode_get_uint16(dnode, "./time-to-learn");
+ struct isis_area *area = nb_running_get_entry(dnode, NULL, true);
+ size_t bufsiz = strlen(area->area_tag) + sizeof("IS-IS Lx");
+ char *buf = XCALLOC(MTYPE_TMP, bufsiz);
+
+ snprintf(buf, bufsiz, "IS-IS %s L1", area->area_tag);
+ spf_backoff_free(area->spf_delay_ietf[0]);
+ area->spf_delay_ietf[0] =
+ spf_backoff_new(master, buf, init_delay, short_delay,
+ long_delay, holddown, timetolearn);
+
+ snprintf(buf, bufsiz, "IS-IS %s L2", area->area_tag);
+ spf_backoff_free(area->spf_delay_ietf[1]);
+ area->spf_delay_ietf[1] =
+ spf_backoff_new(master, buf, init_delay, short_delay,
+ long_delay, holddown, timetolearn);
+
+ XFREE(MTYPE_TMP, buf);
+}
+
+int isis_instance_spf_ietf_backoff_delay_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* All the work is done in the apply_finish */
+ return NB_OK;
+}
+
+int isis_instance_spf_ietf_backoff_delay_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct isis_area *area;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ spf_backoff_free(area->spf_delay_ietf[0]);
+ spf_backoff_free(area->spf_delay_ietf[1]);
+ area->spf_delay_ietf[0] = NULL;
+ area->spf_delay_ietf[1] = NULL;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay/init-delay
+ */
+int isis_instance_spf_ietf_backoff_delay_init_delay_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* All the work is done in the apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay/short-delay
+ */
+int isis_instance_spf_ietf_backoff_delay_short_delay_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* All the work is done in the apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay/long-delay
+ */
+int isis_instance_spf_ietf_backoff_delay_long_delay_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* All the work is done in the apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay/hold-down
+ */
+int isis_instance_spf_ietf_backoff_delay_hold_down_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* All the work is done in the apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay/time-to-learn
+ */
+int isis_instance_spf_ietf_backoff_delay_time_to_learn_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* All the work is done in the apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/spf/minimum-interval/level-1
+ */
+int isis_instance_spf_minimum_interval_level_1_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_area *area;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ area->min_spf_interval[0] = yang_dnode_get_uint16(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/spf/minimum-interval/level-2
+ */
+int isis_instance_spf_minimum_interval_level_2_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_area *area;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ area->min_spf_interval[1] = yang_dnode_get_uint16(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/area-password
+ */
+void area_password_apply_finish(const struct lyd_node *dnode)
+{
+ const char *password = yang_dnode_get_string(dnode, "./password");
+ struct isis_area *area = nb_running_get_entry(dnode, NULL, true);
+ int pass_type = yang_dnode_get_enum(dnode, "./password-type");
+ uint8_t snp_auth = yang_dnode_get_enum(dnode, "./authenticate-snp");
+
+ switch (pass_type) {
+ case ISIS_PASSWD_TYPE_CLEARTXT:
+ isis_area_passwd_cleartext_set(area, IS_LEVEL_1, password,
+ snp_auth);
+ break;
+ case ISIS_PASSWD_TYPE_HMAC_MD5:
+ isis_area_passwd_hmac_md5_set(area, IS_LEVEL_1, password,
+ snp_auth);
+ break;
+ }
+}
+
+int isis_instance_area_password_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* actual setting is done in apply_finish */
+ return NB_OK;
+}
+
+int isis_instance_area_password_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct isis_area *area;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ isis_area_passwd_unset(area, IS_LEVEL_1);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/area-password/password
+ */
+int isis_instance_area_password_password_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* actual setting is done in apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/area-password/password-type
+ */
+int isis_instance_area_password_password_type_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* actual setting is done in apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/area-password/authenticate-snp
+ */
+int isis_instance_area_password_authenticate_snp_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* actual setting is done in apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/domain-password
+ */
+void domain_password_apply_finish(const struct lyd_node *dnode)
+{
+ const char *password = yang_dnode_get_string(dnode, "./password");
+ struct isis_area *area = nb_running_get_entry(dnode, NULL, true);
+ int pass_type = yang_dnode_get_enum(dnode, "./password-type");
+ uint8_t snp_auth = yang_dnode_get_enum(dnode, "./authenticate-snp");
+
+ switch (pass_type) {
+ case ISIS_PASSWD_TYPE_CLEARTXT:
+ isis_area_passwd_cleartext_set(area, IS_LEVEL_2, password,
+ snp_auth);
+ break;
+ case ISIS_PASSWD_TYPE_HMAC_MD5:
+ isis_area_passwd_hmac_md5_set(area, IS_LEVEL_2, password,
+ snp_auth);
+ break;
+ }
+}
+
+int isis_instance_domain_password_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* actual setting is done in apply_finish */
+ return NB_OK;
+}
+
+int isis_instance_domain_password_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct isis_area *area;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ isis_area_passwd_unset(area, IS_LEVEL_2);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/domain-password/password
+ */
+int isis_instance_domain_password_password_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* actual setting is done in apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/domain-password/password-type
+ */
+int isis_instance_domain_password_password_type_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* actual setting is done in apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/domain-password/authenticate-snp
+ */
+int isis_instance_domain_password_authenticate_snp_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* actual setting is done in apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/default-information-originate/ipv4
+ */
+void default_info_origin_apply_finish(const struct lyd_node *dnode, int family)
+{
+ int originate_type = DEFAULT_ORIGINATE;
+ unsigned long metric = 0;
+ const char *routemap = NULL;
+ struct isis_area *area = nb_running_get_entry(dnode, NULL, true);
+ int level = yang_dnode_get_enum(dnode, "./level");
+
+ if (yang_dnode_get_bool(dnode, "./always")) {
+ originate_type = DEFAULT_ORIGINATE_ALWAYS;
+ } else if (family == AF_INET6) {
+ zlog_warn(
+ "%s: Zebra doesn't implement default-originate for IPv6 yet, so use with care or use default-originate always.",
+ __func__);
+ }
+
+ if (yang_dnode_exists(dnode, "./metric"))
+ metric = yang_dnode_get_uint32(dnode, "./metric");
+ if (yang_dnode_exists(dnode, "./route-map"))
+ routemap = yang_dnode_get_string(dnode, "./route-map");
+
+ isis_redist_set(area, level, family, DEFAULT_ROUTE, metric, routemap,
+ originate_type);
+}
+
+void default_info_origin_ipv4_apply_finish(const struct lyd_node *dnode)
+{
+ default_info_origin_apply_finish(dnode, AF_INET);
+}
+
+void default_info_origin_ipv6_apply_finish(const struct lyd_node *dnode)
+{
+ default_info_origin_apply_finish(dnode, AF_INET6);
+}
+
+int isis_instance_default_information_originate_ipv4_create(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* It's all done by default_info_origin_apply_finish */
+ return NB_OK;
+}
+
+int isis_instance_default_information_originate_ipv4_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ struct isis_area *area;
+ int level;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ level = yang_dnode_get_enum(dnode, "./level");
+ isis_redist_unset(area, level, AF_INET, DEFAULT_ROUTE);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/default-information-originate/ipv4/always
+ */
+int isis_instance_default_information_originate_ipv4_always_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* It's all done by default_info_origin_apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/default-information-originate/ipv4/route-map
+ */
+int isis_instance_default_information_originate_ipv4_route_map_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* It's all done by default_info_origin_apply_finish */
+ return NB_OK;
+}
+
+int isis_instance_default_information_originate_ipv4_route_map_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ /* It's all done by default_info_origin_apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/default-information-originate/ipv4/metric
+ */
+int isis_instance_default_information_originate_ipv4_metric_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* It's all done by default_info_origin_apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/default-information-originate/ipv6
+ */
+int isis_instance_default_information_originate_ipv6_create(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* It's all done by default_info_origin_apply_finish */
+ return NB_OK;
+}
+
+int isis_instance_default_information_originate_ipv6_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ struct isis_area *area;
+ int level;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ level = yang_dnode_get_enum(dnode, "./level");
+ isis_redist_unset(area, level, AF_INET6, DEFAULT_ROUTE);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/default-information-originate/ipv6/always
+ */
+int isis_instance_default_information_originate_ipv6_always_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* It's all done by default_info_origin_apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/default-information-originate/ipv6/route-map
+ */
+int isis_instance_default_information_originate_ipv6_route_map_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* It's all done by default_info_origin_apply_finish */
+ return NB_OK;
+}
+
+int isis_instance_default_information_originate_ipv6_route_map_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ /* It's all done by default_info_origin_apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/default-information-originate/ipv6/metric
+ */
+int isis_instance_default_information_originate_ipv6_metric_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* It's all done by default_info_origin_apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/redistribute/ipv4
+ */
+void redistribute_apply_finish(const struct lyd_node *dnode, int family)
+{
+ assert(family == AF_INET || family == AF_INET6);
+ int type, level;
+ unsigned long metric = 0;
+ const char *routemap = NULL;
+ struct isis_area *area;
+
+ type = yang_dnode_get_enum(dnode, "./protocol");
+ level = yang_dnode_get_enum(dnode, "./level");
+ area = nb_running_get_entry(dnode, NULL, true);
+
+ if (yang_dnode_exists(dnode, "./metric"))
+ metric = yang_dnode_get_uint32(dnode, "./metric");
+ if (yang_dnode_exists(dnode, "./route-map"))
+ routemap = yang_dnode_get_string(dnode, "./route-map");
+
+ isis_redist_set(area, level, family, type, metric, routemap, 0);
+}
+
+void redistribute_ipv4_apply_finish(const struct lyd_node *dnode)
+{
+ redistribute_apply_finish(dnode, AF_INET);
+}
+
+void redistribute_ipv6_apply_finish(const struct lyd_node *dnode)
+{
+ redistribute_apply_finish(dnode, AF_INET6);
+}
+
+int isis_instance_redistribute_ipv4_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* It's all done by redistribute_apply_finish */
+ return NB_OK;
+}
+
+int isis_instance_redistribute_ipv4_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct isis_area *area;
+ int level, type;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ level = yang_dnode_get_enum(dnode, "./level");
+ type = yang_dnode_get_enum(dnode, "./protocol");
+ isis_redist_unset(area, level, AF_INET, type);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/redistribute/ipv4/route-map
+ */
+int isis_instance_redistribute_ipv4_route_map_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* It's all done by redistribute_apply_finish */
+ return NB_OK;
+}
+
+int isis_instance_redistribute_ipv4_route_map_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ /* It's all done by redistribute_apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/redistribute/ipv4/metric
+ */
+int isis_instance_redistribute_ipv4_metric_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* It's all done by redistribute_apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/redistribute/ipv6
+ */
+int isis_instance_redistribute_ipv6_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* It's all done by redistribute_apply_finish */
+ return NB_OK;
+}
+
+int isis_instance_redistribute_ipv6_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct isis_area *area;
+ int level, type;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ level = yang_dnode_get_enum(dnode, "./level");
+ type = yang_dnode_get_enum(dnode, "./protocol");
+ isis_redist_unset(area, level, AF_INET6, type);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/redistribute/ipv6/route-map
+ */
+int isis_instance_redistribute_ipv6_route_map_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* It's all done by redistribute_apply_finish */
+ return NB_OK;
+}
+
+int isis_instance_redistribute_ipv6_route_map_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ /* It's all done by redistribute_apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/redistribute/ipv6/metric
+ */
+int isis_instance_redistribute_ipv6_metric_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ /* It's all done by redistribute_apply_finish */
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/multi-topology/ipv4-multicast
+ */
+static int isis_multi_topology_common(enum nb_event event,
+ const struct lyd_node *dnode,
+ const char *topology, bool create)
+{
+ struct isis_area *area;
+ struct isis_area_mt_setting *setting;
+ uint16_t mtid = isis_str2mtid(topology);
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ if (mtid == (uint16_t)-1) {
+ flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
+ "Unknown topology %s", topology);
+ return NB_ERR_VALIDATION;
+ }
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ area = nb_running_get_entry(dnode, NULL, true);
+ setting = area_get_mt_setting(area, mtid);
+ setting->enabled = create;
+ lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 0);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int isis_multi_topology_overload_common(enum nb_event event,
+ const struct lyd_node *dnode,
+ const char *topology)
+{
+ struct isis_area *area;
+ struct isis_area_mt_setting *setting;
+ uint16_t mtid = isis_str2mtid(topology);
+
+ /* validation is done in isis_multi_topology_common */
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ setting = area_get_mt_setting(area, mtid);
+ setting->overload = yang_dnode_get_bool(dnode, NULL);
+ if (setting->enabled)
+ lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 0);
+
+ return NB_OK;
+}
+
+int isis_instance_multi_topology_ipv4_multicast_create(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return isis_multi_topology_common(event, dnode, "ipv4-multicast", true);
+}
+
+int isis_instance_multi_topology_ipv4_multicast_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ return isis_multi_topology_common(event, dnode, "ipv4-multicast",
+ false);
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/multi-topology/ipv4-multicast/overload
+ */
+int isis_instance_multi_topology_ipv4_multicast_overload_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return isis_multi_topology_overload_common(event, dnode,
+ "ipv4-multicast");
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/multi-topology/ipv4-management
+ */
+int isis_instance_multi_topology_ipv4_management_create(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return isis_multi_topology_common(event, dnode, "ipv4-mgmt", true);
+}
+
+int isis_instance_multi_topology_ipv4_management_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ return isis_multi_topology_common(event, dnode, "ipv4-mgmt", false);
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/multi-topology/ipv4-management/overload
+ */
+int isis_instance_multi_topology_ipv4_management_overload_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return isis_multi_topology_overload_common(event, dnode, "ipv4-mgmt");
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-unicast
+ */
+int isis_instance_multi_topology_ipv6_unicast_create(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return isis_multi_topology_common(event, dnode, "ipv6-unicast", true);
+}
+
+int isis_instance_multi_topology_ipv6_unicast_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ return isis_multi_topology_common(event, dnode, "ipv6-unicast", false);
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-unicast/overload
+ */
+int isis_instance_multi_topology_ipv6_unicast_overload_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return isis_multi_topology_overload_common(event, dnode,
+ "ipv6-unicast");
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-multicast
+ */
+int isis_instance_multi_topology_ipv6_multicast_create(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return isis_multi_topology_common(event, dnode, "ipv6-multicast", true);
+}
+
+int isis_instance_multi_topology_ipv6_multicast_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ return isis_multi_topology_common(event, dnode, "ipv6-multicast",
+ false);
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-multicast/overload
+ */
+int isis_instance_multi_topology_ipv6_multicast_overload_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return isis_multi_topology_overload_common(event, dnode,
+ "ipv6-multicast");
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-management
+ */
+int isis_instance_multi_topology_ipv6_management_create(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return isis_multi_topology_common(event, dnode, "ipv6-mgmt", true);
+}
+
+int isis_instance_multi_topology_ipv6_management_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ return isis_multi_topology_common(event, dnode, "ipv6-mgmt", false);
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-management/overload
+ */
+int isis_instance_multi_topology_ipv6_management_overload_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return isis_multi_topology_overload_common(event, dnode, "ipv6-mgmt");
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-dstsrc
+ */
+int isis_instance_multi_topology_ipv6_dstsrc_create(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return isis_multi_topology_common(event, dnode, "ipv6-dstsrc", true);
+}
+
+int isis_instance_multi_topology_ipv6_dstsrc_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ return isis_multi_topology_common(event, dnode, "ipv6-dstsrc", false);
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-dstsrc/overload
+ */
+int isis_instance_multi_topology_ipv6_dstsrc_overload_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return isis_multi_topology_overload_common(event, dnode, "ipv6-dstsrc");
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/log-adjacency-changes
+ */
+int isis_instance_log_adjacency_changes_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_area *area;
+ bool log = yang_dnode_get_bool(dnode, NULL);
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ area->log_adj_changes = log ? 1 : 0;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/mpls-te
+ */
+int isis_instance_mpls_te_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct listnode *node;
+ struct isis_area *area;
+ struct isis_circuit *circuit;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ if (area->mta == NULL) {
+
+ struct mpls_te_area *new;
+
+ zlog_debug("ISIS-TE(%s): Initialize MPLS Traffic Engineering",
+ area->area_tag);
+
+ new = XCALLOC(MTYPE_ISIS_MPLS_TE, sizeof(struct mpls_te_area));
+
+ /* Initialize MPLS_TE structure */
+ new->status = enable;
+ new->level = 0;
+ new->inter_as = off;
+ new->interas_areaid.s_addr = 0;
+ new->router_id.s_addr = 0;
+
+ area->mta = new;
+ } else {
+ area->mta->status = enable;
+ }
+
+ /* Update Extended TLVs according to Interface link parameters */
+ for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit))
+ isis_link_params_update(circuit, circuit->interface);
+
+ /* Reoriginate STD_TE & GMPLS circuits */
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+int isis_instance_mpls_te_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct listnode *node;
+ struct isis_area *area;
+ struct isis_circuit *circuit;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ if (IS_MPLS_TE(area->mta))
+ area->mta->status = disable;
+ else
+ return NB_OK;
+
+ /* Flush LSP if circuit engage */
+ for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
+ if (!IS_EXT_TE(circuit->ext))
+ continue;
+
+ /* disable MPLS_TE Circuit keeping SR one's */
+ if (IS_SUBTLV(circuit->ext, EXT_ADJ_SID))
+ circuit->ext->status = EXT_ADJ_SID;
+ else if (IS_SUBTLV(circuit->ext, EXT_LAN_ADJ_SID))
+ circuit->ext->status = EXT_LAN_ADJ_SID;
+ else
+ circuit->ext->status = 0;
+ }
+
+ /* Reoriginate STD_TE & GMPLS circuits */
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ zlog_debug("ISIS-TE(%s): Disabled MPLS Traffic Engineering",
+ area->area_tag);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/mpls-te/router-address
+ */
+int isis_instance_mpls_te_router_address_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct in_addr value;
+ struct isis_area *area;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ /* only proceed if MPLS-TE is enabled */
+ if (!IS_MPLS_TE(area->mta))
+ return NB_OK;
+
+ /* Update Area Router ID */
+ yang_dnode_get_ipv4(&value, dnode, NULL);
+ area->mta->router_id.s_addr = value.s_addr;
+
+ /* And re-schedule LSP update */
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+int isis_instance_mpls_te_router_address_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct isis_area *area;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(dnode, NULL, true);
+ /* only proceed if MPLS-TE is enabled */
+ if (!IS_MPLS_TE(area->mta))
+ return NB_OK;
+
+ /* Reset Area Router ID */
+ area->mta->router_id.s_addr = INADDR_ANY;
+
+ /* And re-schedule LSP update */
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis
+ */
+int lib_interface_isis_create(enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_area *area;
+ struct interface *ifp;
+ struct isis_circuit *circuit;
+ const char *area_tag = yang_dnode_get_string(dnode, "./area-tag");
+ uint32_t min_mtu, actual_mtu;
+
+ switch (event) {
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_VALIDATE:
+ /* check if interface mtu is sufficient. If the area has not
+ * been created yet, assume default MTU for the area
+ */
+ ifp = nb_running_get_entry(dnode, NULL, false);
+ /* zebra might not know yet about the MTU - nothing we can do */
+ if (!ifp || ifp->mtu == 0)
+ break;
+ actual_mtu =
+ if_is_broadcast(ifp) ? ifp->mtu - LLC_LEN : ifp->mtu;
+ area = isis_area_lookup(area_tag);
+ if (area)
+ min_mtu = area->lsp_mtu;
+ else
+#ifndef FABRICD
+ min_mtu = yang_get_default_uint16(
+ "/frr-isisd:isis/instance/lsp/mtu");
+#else
+ min_mtu = DEFAULT_LSP_MTU;
+#endif /* ifndef FABRICD */
+ if (actual_mtu < min_mtu) {
+ flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
+ "Interface %s has MTU %" PRIu32
+ ", minimum MTU for the area is %" PRIu32 "",
+ ifp->name, actual_mtu, min_mtu);
+ return NB_ERR_VALIDATION;
+ }
+ break;
+ case NB_EV_APPLY:
+ area = isis_area_lookup(area_tag);
+ /* The area should have already be created. We are
+ * setting the priority of the global isis area creation
+ * slightly lower, so it should be executed first, but I
+ * cannot rely on that so here I have to check.
+ */
+ if (!area) {
+ flog_err(
+ EC_LIB_NB_CB_CONFIG_APPLY,
+ "%s: attempt to create circuit for area %s before the area has been created",
+ __func__, area_tag);
+ abort();
+ }
+
+ ifp = nb_running_get_entry(dnode, NULL, true);
+ circuit = isis_circuit_create(area, ifp);
+ assert(circuit
+ && (circuit->state == C_STATE_CONF
+ || circuit->state == C_STATE_UP));
+ nb_running_set_entry(dnode, circuit);
+ break;
+ }
+
+ return NB_OK;
+}
+
+int lib_interface_isis_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct isis_circuit *circuit;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_unset_entry(dnode);
+ if (!circuit)
+ return NB_ERR_INCONSISTENCY;
+ if (circuit->state == C_STATE_UP || circuit->state == C_STATE_CONF)
+ isis_csm_state_change(ISIS_DISABLE, circuit, circuit->area);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/area-tag
+ */
+int lib_interface_isis_area_tag_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_circuit *circuit;
+ struct interface *ifp;
+ struct vrf *vrf;
+ const char *area_tag, *ifname, *vrfname;
+
+ if (event == NB_EV_VALIDATE) {
+ /* libyang doesn't like relative paths across module boundaries
+ */
+ ifname = yang_dnode_get_string(dnode->parent->parent, "./name");
+ vrfname = yang_dnode_get_string(dnode->parent->parent, "./vrf");
+ vrf = vrf_lookup_by_name(vrfname);
+ assert(vrf);
+ ifp = if_lookup_by_name(ifname, vrf->vrf_id);
+ if (!ifp)
+ return NB_OK;
+ circuit = circuit_lookup_by_ifp(ifp, isis->init_circ_list);
+ area_tag = yang_dnode_get_string(dnode, NULL);
+ if (circuit && circuit->area && circuit->area->area_tag
+ && strcmp(circuit->area->area_tag, area_tag)) {
+ flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
+ "ISIS circuit is already defined on %s",
+ circuit->area->area_tag);
+ return NB_ERR_VALIDATION;
+ }
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/circuit-type
+ */
+int lib_interface_isis_circuit_type_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ int circ_type = yang_dnode_get_enum(dnode, NULL);
+ struct isis_circuit *circuit;
+ struct interface *ifp;
+ struct vrf *vrf;
+ const char *ifname, *vrfname;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ /* libyang doesn't like relative paths across module boundaries
+ */
+ ifname = yang_dnode_get_string(dnode->parent->parent, "./name");
+ vrfname = yang_dnode_get_string(dnode->parent->parent, "./vrf");
+ vrf = vrf_lookup_by_name(vrfname);
+ assert(vrf);
+ ifp = if_lookup_by_name(ifname, vrf->vrf_id);
+ if (!ifp)
+ break;
+ circuit = circuit_lookup_by_ifp(ifp, isis->init_circ_list);
+ if (circuit && circuit->state == C_STATE_UP
+ && circuit->area->is_type != IS_LEVEL_1_AND_2
+ && circuit->area->is_type != circ_type) {
+ flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
+ "Invalid circuit level for area %s",
+ circuit->area->area_tag);
+ return NB_ERR_VALIDATION;
+ }
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ isis_circuit_is_type_set(circuit, circ_type);
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/ipv4-routing
+ */
+int lib_interface_isis_ipv4_routing_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ bool ipv4, ipv6;
+ struct isis_circuit *circuit;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ ipv4 = yang_dnode_get_bool(dnode, NULL);
+ ipv6 = yang_dnode_get_bool(dnode, "../ipv6-routing");
+ isis_circuit_af_set(circuit, ipv4, ipv6);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/ipv6-routing
+ */
+int lib_interface_isis_ipv6_routing_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ bool ipv4, ipv6;
+ struct isis_circuit *circuit;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ ipv4 = yang_dnode_exists(dnode, "../ipv4-routing");
+ ipv6 = yang_dnode_get_bool(dnode, NULL);
+ isis_circuit_af_set(circuit, ipv4, ipv6);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring
+ */
+int lib_interface_isis_bfd_monitoring_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_circuit *circuit;
+ bool bfd_monitoring;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ bfd_monitoring = yang_dnode_get_bool(dnode, NULL);
+
+ if (bfd_monitoring) {
+ isis_bfd_circuit_param_set(circuit, BFD_DEF_MIN_RX,
+ BFD_DEF_MIN_TX, BFD_DEF_DETECT_MULT,
+ true);
+ } else {
+ isis_bfd_circuit_cmd(circuit, ZEBRA_BFD_DEST_DEREGISTER);
+ bfd_info_free(&circuit->bfd_info);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/csnp-interval/level-1
+ */
+int lib_interface_isis_csnp_interval_level_1_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_circuit *circuit;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ circuit->csnp_interval[0] = yang_dnode_get_uint16(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/csnp-interval/level-2
+ */
+int lib_interface_isis_csnp_interval_level_2_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_circuit *circuit;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ circuit->csnp_interval[1] = yang_dnode_get_uint16(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/psnp-interval/level-1
+ */
+int lib_interface_isis_psnp_interval_level_1_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_circuit *circuit;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ circuit->psnp_interval[0] = yang_dnode_get_uint16(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/psnp-interval/level-2
+ */
+int lib_interface_isis_psnp_interval_level_2_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_circuit *circuit;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ circuit->psnp_interval[1] = yang_dnode_get_uint16(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/padding
+ */
+int lib_interface_isis_hello_padding_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_circuit *circuit;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ circuit->pad_hellos = yang_dnode_get_bool(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-1
+ */
+int lib_interface_isis_hello_interval_level_1_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_circuit *circuit;
+ uint32_t interval;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ interval = yang_dnode_get_uint32(dnode, NULL);
+ circuit->hello_interval[0] = interval;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-2
+ */
+int lib_interface_isis_hello_interval_level_2_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_circuit *circuit;
+ uint32_t interval;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ interval = yang_dnode_get_uint32(dnode, NULL);
+ circuit->hello_interval[1] = interval;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/multiplier/level-1
+ */
+int lib_interface_isis_hello_multiplier_level_1_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_circuit *circuit;
+ uint16_t multi;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ multi = yang_dnode_get_uint16(dnode, NULL);
+ circuit->hello_multiplier[0] = multi;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/multiplier/level-2
+ */
+int lib_interface_isis_hello_multiplier_level_2_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_circuit *circuit;
+ uint16_t multi;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ multi = yang_dnode_get_uint16(dnode, NULL);
+ circuit->hello_multiplier[1] = multi;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/metric/level-1
+ */
+int lib_interface_isis_metric_level_1_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_circuit *circuit;
+ unsigned int met;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ met = yang_dnode_get_uint32(dnode, NULL);
+ isis_circuit_metric_set(circuit, IS_LEVEL_1, met);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/metric/level-2
+ */
+int lib_interface_isis_metric_level_2_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_circuit *circuit;
+ unsigned int met;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ met = yang_dnode_get_uint32(dnode, NULL);
+ isis_circuit_metric_set(circuit, IS_LEVEL_2, met);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/priority/level-1
+ */
+int lib_interface_isis_priority_level_1_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_circuit *circuit;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ circuit->priority[0] = yang_dnode_get_uint8(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/priority/level-2
+ */
+int lib_interface_isis_priority_level_2_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_circuit *circuit;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ circuit->priority[1] = yang_dnode_get_uint8(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/network-type
+ */
+int lib_interface_isis_network_type_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_circuit *circuit;
+ int net_type = yang_dnode_get_enum(dnode, NULL);
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ circuit = nb_running_get_entry(dnode, NULL, false);
+ if (!circuit)
+ break;
+ if (circuit->circ_type == CIRCUIT_T_LOOPBACK) {
+ flog_warn(
+ EC_LIB_NB_CB_CONFIG_VALIDATE,
+ "Cannot change network type on loopback interface");
+ return NB_ERR_VALIDATION;
+ }
+ if (net_type == CIRCUIT_T_BROADCAST
+ && circuit->state == C_STATE_UP
+ && !if_is_broadcast(circuit->interface)) {
+ flog_warn(
+ EC_LIB_NB_CB_CONFIG_VALIDATE,
+ "Cannot configure non-broadcast interface for broadcast operation");
+ return NB_ERR_VALIDATION;
+ }
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ isis_circuit_circ_type_set(circuit, net_type);
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/passive
+ */
+int lib_interface_isis_passive_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_circuit *circuit;
+ struct isis_area *area;
+ struct interface *ifp;
+ bool passive = yang_dnode_get_bool(dnode, NULL);
+
+ /* validation only applies if we are setting passive to false */
+ if (!passive && event == NB_EV_VALIDATE) {
+ circuit = nb_running_get_entry(dnode, NULL, false);
+ if (!circuit)
+ return NB_OK;
+ ifp = circuit->interface;
+ if (!ifp)
+ return NB_OK;
+ if (if_is_loopback(ifp)) {
+ flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
+ "Loopback is always passive");
+ return NB_ERR_VALIDATION;
+ }
+ }
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ if (circuit->state != C_STATE_UP) {
+ circuit->is_passive = passive;
+ } else {
+ area = circuit->area;
+ isis_csm_state_change(ISIS_DISABLE, circuit, area);
+ circuit->is_passive = passive;
+ isis_csm_state_change(ISIS_ENABLE, circuit, area);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/password
+ */
+int lib_interface_isis_password_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return NB_OK;
+}
+
+int lib_interface_isis_password_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct isis_circuit *circuit;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ isis_circuit_passwd_unset(circuit);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/password/password
+ */
+int lib_interface_isis_password_password_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_circuit *circuit;
+ const char *password;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ password = yang_dnode_get_string(dnode, NULL);
+ circuit = nb_running_get_entry(dnode, NULL, true);
+
+ isis_circuit_passwd_set(circuit, circuit->passwd.type, password);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/password/password-type
+ */
+int lib_interface_isis_password_password_type_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_circuit *circuit;
+ uint8_t pass_type;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ pass_type = yang_dnode_get_enum(dnode, NULL);
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ circuit->passwd.type = pass_type;
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/disable-three-way-handshake
+ */
+int lib_interface_isis_disable_three_way_handshake_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct isis_circuit *circuit;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ circuit->disable_threeway_adj = yang_dnode_get_bool(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-unicast
+ */
+static int lib_interface_isis_multi_topology_common(
+ enum nb_event event, const struct lyd_node *dnode, uint16_t mtid)
+{
+ struct isis_circuit *circuit;
+ bool value;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ circuit = nb_running_get_entry(dnode, NULL, false);
+ if (circuit && circuit->area && circuit->area->oldmetric) {
+ flog_warn(
+ EC_LIB_NB_CB_CONFIG_VALIDATE,
+ "Multi topology IS-IS can only be used with wide metrics");
+ return NB_ERR_VALIDATION;
+ }
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ circuit = nb_running_get_entry(dnode, NULL, true);
+ value = yang_dnode_get_bool(dnode, NULL);
+ isis_circuit_mt_enabled_set(circuit, mtid, value);
+ break;
+ }
+
+ return NB_OK;
+}
+
+int lib_interface_isis_multi_topology_ipv4_unicast_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return lib_interface_isis_multi_topology_common(event, dnode,
+ ISIS_MT_IPV4_UNICAST);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-multicast
+ */
+int lib_interface_isis_multi_topology_ipv4_multicast_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return lib_interface_isis_multi_topology_common(event, dnode,
+ ISIS_MT_IPV4_MULTICAST);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-management
+ */
+int lib_interface_isis_multi_topology_ipv4_management_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return lib_interface_isis_multi_topology_common(event, dnode,
+ ISIS_MT_IPV4_MGMT);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-unicast
+ */
+int lib_interface_isis_multi_topology_ipv6_unicast_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return lib_interface_isis_multi_topology_common(event, dnode,
+ ISIS_MT_IPV6_UNICAST);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-multicast
+ */
+int lib_interface_isis_multi_topology_ipv6_multicast_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return lib_interface_isis_multi_topology_common(event, dnode,
+ ISIS_MT_IPV6_MULTICAST);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-management
+ */
+int lib_interface_isis_multi_topology_ipv6_management_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return lib_interface_isis_multi_topology_common(event, dnode,
+ ISIS_MT_IPV6_MGMT);
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-dstsrc
+ */
+int lib_interface_isis_multi_topology_ipv6_dstsrc_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return lib_interface_isis_multi_topology_common(event, dnode,
+ ISIS_MT_IPV6_DSTSRC);
+}
diff --git a/isisd/isis_nb_notifications.c b/isisd/isis_nb_notifications.c
new file mode 100644
index 0000000000..ea33ec10ec
--- /dev/null
+++ b/isisd/isis_nb_notifications.c
@@ -0,0 +1,491 @@
+/*
+ * Copyright (C) 2018 Volta Networks
+ * Emanuele Di Pascale
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "northbound.h"
+#include "log.h"
+
+#include "isisd/isisd.h"
+#include "isisd/isis_nb.h"
+#include "isisd/isis_adjacency.h"
+#include "isisd/isis_dynhn.h"
+#include "isisd/isis_misc.h"
+
+/*
+ * Helper functions.
+ */
+static void notif_prep_instance_hdr(const char *xpath,
+ const struct isis_area *area,
+ const char *routing_instance,
+ struct list *args)
+{
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/routing-instance", xpath);
+ data = yang_data_new_string(xpath_arg, routing_instance);
+ listnode_add(args, data);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/routing-protocol-name",
+ xpath);
+ data = yang_data_new_string(xpath_arg, area->area_tag);
+ listnode_add(args, data);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/isis-level", xpath);
+ data = yang_data_new_enum(xpath_arg, area->is_type);
+ listnode_add(args, data);
+}
+
+static void notif_prepr_iface_hdr(const char *xpath,
+ const struct isis_circuit *circuit,
+ struct list *args)
+{
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/interface-name", xpath);
+ data = yang_data_new_string(xpath_arg, circuit->interface->name);
+ listnode_add(args, data);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/interface-level", xpath);
+ data = yang_data_new_enum(xpath_arg, circuit->is_type);
+ listnode_add(args, data);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/extended-circuit-id", xpath);
+ /* we do not seem to have the extended version of the circuit_id */
+ data = yang_data_new_uint32(xpath_arg, (uint32_t)circuit->circuit_id);
+ listnode_add(args, data);
+}
+
+/*
+ * XPath: /frr-isisd:database-overload
+ */
+void isis_notif_db_overload(const struct isis_area *area, bool overload)
+{
+ const char *xpath = "/frr-isisd:database-overload";
+ struct list *arguments = yang_data_list_new();
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+
+ notif_prep_instance_hdr(xpath, area, "default", arguments);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/overload", xpath);
+ data = yang_data_new_enum(xpath_arg, !!overload);
+ listnode_add(arguments, data);
+
+ nb_notification_send(xpath, arguments);
+}
+
+/*
+ * XPath: /frr-isisd:lsp-too-large
+ */
+void isis_notif_lsp_too_large(const struct isis_circuit *circuit,
+ uint32_t pdu_size, const char *lsp_id)
+{
+ const char *xpath = "/frr-isisd:lsp-too-large";
+ struct list *arguments = yang_data_list_new();
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+ struct isis_area *area = circuit->area;
+
+ notif_prep_instance_hdr(xpath, area, "default", arguments);
+ notif_prepr_iface_hdr(xpath, circuit, arguments);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/pdu-size", xpath);
+ data = yang_data_new_uint32(xpath_arg, pdu_size);
+ listnode_add(arguments, data);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
+ data = yang_data_new_string(xpath_arg, lsp_id);
+ listnode_add(arguments, data);
+
+ nb_notification_send(xpath, arguments);
+}
+
+/*
+ * XPath: /frr-isisd:if-state-change
+ */
+void isis_notif_if_state_change(const struct isis_circuit *circuit, bool down)
+{
+ const char *xpath = "/frr-isisd:if-state-change";
+ struct list *arguments = yang_data_list_new();
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+ struct isis_area *area = circuit->area;
+
+ notif_prep_instance_hdr(xpath, area, "default", arguments);
+ notif_prepr_iface_hdr(xpath, circuit, arguments);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/state", xpath);
+ data = yang_data_new_enum(xpath_arg, !!down);
+ listnode_add(arguments, data);
+
+ nb_notification_send(xpath, arguments);
+}
+
+/*
+ * XPath: /frr-isisd:corrupted-lsp-detected
+ */
+void isis_notif_corrupted_lsp(const struct isis_area *area, const char *lsp_id)
+{
+ const char *xpath = "/frr-isisd:corrupted-lsp-detected";
+ struct list *arguments = yang_data_list_new();
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+
+ notif_prep_instance_hdr(xpath, area, "default", arguments);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
+ data = yang_data_new_string(xpath_arg, lsp_id);
+ listnode_add(arguments, data);
+
+ nb_notification_send(xpath, arguments);
+}
+
+/*
+ * XPath: /frr-isisd:attempt-to-exceed-max-sequence
+ */
+void isis_notif_lsp_exceed_max(const struct isis_area *area, const char *lsp_id)
+{
+ const char *xpath = "/frr-isisd:attempt-to-exceed-max-sequence";
+ struct list *arguments = yang_data_list_new();
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+
+ notif_prep_instance_hdr(xpath, area, "default", arguments);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
+ data = yang_data_new_string(xpath_arg, lsp_id);
+ listnode_add(arguments, data);
+
+ nb_notification_send(xpath, arguments);
+}
+
+/*
+ * XPath: /frr-isisd:max-area-addresses-mismatch
+ */
+void isis_notif_max_area_addr_mismatch(const struct isis_circuit *circuit,
+ uint8_t max_area_addrs,
+ const char *raw_pdu)
+{
+ const char *xpath = "/frr-isisd:max-area-addresses-mismatch";
+ struct list *arguments = yang_data_list_new();
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+ struct isis_area *area = circuit->area;
+
+ notif_prep_instance_hdr(xpath, area, "default", arguments);
+ notif_prepr_iface_hdr(xpath, circuit, arguments);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/max-area-addresses", xpath);
+ data = yang_data_new_uint8(xpath_arg, max_area_addrs);
+ listnode_add(arguments, data);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath);
+ data = yang_data_new(xpath_arg, raw_pdu);
+ listnode_add(arguments, data);
+
+ nb_notification_send(xpath, arguments);
+}
+
+/*
+ * XPath: /frr-isisd:authentication-type-failure
+ */
+void isis_notif_authentication_type_failure(const struct isis_circuit *circuit,
+ const char *raw_pdu)
+{
+ const char *xpath = "/frr-isisd:authentication-type-failure";
+ struct list *arguments = yang_data_list_new();
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+ struct isis_area *area = circuit->area;
+
+ notif_prep_instance_hdr(xpath, area, "default", arguments);
+ notif_prepr_iface_hdr(xpath, circuit, arguments);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath);
+ data = yang_data_new(xpath_arg, raw_pdu);
+ listnode_add(arguments, data);
+
+ nb_notification_send(xpath, arguments);
+}
+
+/*
+ * XPath: /frr-isisd:authentication-failure
+ */
+void isis_notif_authentication_failure(const struct isis_circuit *circuit,
+ const char *raw_pdu)
+{
+ const char *xpath = "/frr-isisd:authentication-failure";
+ struct list *arguments = yang_data_list_new();
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+ struct isis_area *area = circuit->area;
+
+ notif_prep_instance_hdr(xpath, area, "default", arguments);
+ notif_prepr_iface_hdr(xpath, circuit, arguments);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath);
+ data = yang_data_new(xpath_arg, raw_pdu);
+ listnode_add(arguments, data);
+
+ nb_notification_send(xpath, arguments);
+}
+
+/*
+ * XPath: /frr-isisd:adjacency-state-change
+ */
+void isis_notif_adj_state_change(const struct isis_adjacency *adj,
+ int new_state, const char *reason)
+{
+ const char *xpath = "/frr-isisd:adjacency-state-change";
+ struct list *arguments = yang_data_list_new();
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+ struct isis_circuit *circuit = adj->circuit;
+ struct isis_area *area = circuit->area;
+ struct isis_dynhn *dyn = dynhn_find_by_id(adj->sysid);
+
+ notif_prep_instance_hdr(xpath, area, "default", arguments);
+ notif_prepr_iface_hdr(xpath, circuit, arguments);
+ if (dyn) {
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/neighbor", xpath);
+ data = yang_data_new_string(xpath_arg, dyn->hostname);
+ listnode_add(arguments, data);
+ }
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/neighbor-system-id", xpath);
+ data = yang_data_new_string(xpath_arg, sysid_print(adj->sysid));
+ listnode_add(arguments, data);
+
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/state", xpath);
+ data = yang_data_new_string(xpath_arg, isis_adj_yang_state(new_state));
+ listnode_add(arguments, data);
+ if (new_state == ISIS_ADJ_DOWN) {
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/reason", xpath);
+ data = yang_data_new_string(xpath_arg, reason);
+ listnode_add(arguments, data);
+ }
+
+ nb_notification_send(xpath, arguments);
+}
+
+/*
+ * XPath: /frr-isisd:rejected-adjacency
+ */
+void isis_notif_reject_adjacency(const struct isis_circuit *circuit,
+ const char *reason, const char *raw_pdu)
+{
+ const char *xpath = "/frr-isisd:rejected-adjacency";
+ struct list *arguments = yang_data_list_new();
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+ struct isis_area *area = circuit->area;
+
+ notif_prep_instance_hdr(xpath, area, "default", arguments);
+ notif_prepr_iface_hdr(xpath, circuit, arguments);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/reason", xpath);
+ data = yang_data_new_string(xpath_arg, reason);
+ listnode_add(arguments, data);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath);
+ data = yang_data_new(xpath_arg, raw_pdu);
+ listnode_add(arguments, data);
+
+ nb_notification_send(xpath, arguments);
+}
+
+/*
+ * XPath: /frr-isisd:area-mismatch
+ */
+void isis_notif_area_mismatch(const struct isis_circuit *circuit,
+ const char *raw_pdu)
+{
+ const char *xpath = "/frr-isisd:area-mismatch";
+ struct list *arguments = yang_data_list_new();
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+ struct isis_area *area = circuit->area;
+
+ notif_prep_instance_hdr(xpath, area, "default", arguments);
+ notif_prepr_iface_hdr(xpath, circuit, arguments);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath);
+ data = yang_data_new(xpath_arg, raw_pdu);
+ listnode_add(arguments, data);
+
+ nb_notification_send(xpath, arguments);
+}
+
+/*
+ * XPath: /frr-isisd:lsp-received
+ */
+void isis_notif_lsp_received(const struct isis_circuit *circuit,
+ const char *lsp_id, uint32_t seqno,
+ uint32_t timestamp, const char *sys_id)
+{
+ const char *xpath = "/frr-isisd:lsp-received";
+ struct list *arguments = yang_data_list_new();
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+ struct isis_area *area = circuit->area;
+
+ notif_prep_instance_hdr(xpath, area, "default", arguments);
+ notif_prepr_iface_hdr(xpath, circuit, arguments);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
+ data = yang_data_new_string(xpath_arg, lsp_id);
+ listnode_add(arguments, data);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/sequence", xpath);
+ data = yang_data_new_uint32(xpath_arg, seqno);
+ listnode_add(arguments, data);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/received-timestamp", xpath);
+ data = yang_data_new_uint32(xpath_arg, timestamp);
+ listnode_add(arguments, data);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/neighbor-system-id", xpath);
+ data = yang_data_new_string(xpath_arg, sys_id);
+ listnode_add(arguments, data);
+
+ nb_notification_send(xpath, arguments);
+}
+
+/*
+ * XPath: /frr-isisd:lsp-generation
+ */
+void isis_notif_lsp_gen(const struct isis_area *area, const char *lsp_id,
+ uint32_t seqno, uint32_t timestamp)
+{
+ const char *xpath = "/frr-isisd:lsp-generation";
+ struct list *arguments = yang_data_list_new();
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+
+ notif_prep_instance_hdr(xpath, area, "default", arguments);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
+ data = yang_data_new_string(xpath_arg, lsp_id);
+ listnode_add(arguments, data);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/sequence", xpath);
+ data = yang_data_new_uint32(xpath_arg, seqno);
+ listnode_add(arguments, data);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/send-timestamp", xpath);
+ data = yang_data_new_uint32(xpath_arg, timestamp);
+ listnode_add(arguments, data);
+
+ nb_notification_send(xpath, arguments);
+}
+
+/*
+ * XPath: /frr-isisd:id-len-mismatch
+ */
+void isis_notif_id_len_mismatch(const struct isis_circuit *circuit,
+ uint8_t rcv_id_len, const char *raw_pdu)
+{
+ const char *xpath = "/frr-isisd:id-len-mismatch";
+ struct list *arguments = yang_data_list_new();
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+ struct isis_area *area = circuit->area;
+
+ notif_prep_instance_hdr(xpath, area, "default", arguments);
+ notif_prepr_iface_hdr(xpath, circuit, arguments);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/pdu-field-len", xpath);
+ data = yang_data_new_uint8(xpath_arg, rcv_id_len);
+ listnode_add(arguments, data);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath);
+ data = yang_data_new(xpath_arg, raw_pdu);
+ listnode_add(arguments, data);
+
+ nb_notification_send(xpath, arguments);
+}
+
+/*
+ * XPath: /frr-isisd:version-skew
+ */
+void isis_notif_version_skew(const struct isis_circuit *circuit,
+ uint8_t version, const char *raw_pdu)
+{
+ const char *xpath = "/frr-isisd:version-skew";
+ struct list *arguments = yang_data_list_new();
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+ struct isis_area *area = circuit->area;
+
+ notif_prep_instance_hdr(xpath, area, "default", arguments);
+ notif_prepr_iface_hdr(xpath, circuit, arguments);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/protocol-version", xpath);
+ data = yang_data_new_uint8(xpath_arg, version);
+ listnode_add(arguments, data);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath);
+ data = yang_data_new(xpath_arg, raw_pdu);
+ listnode_add(arguments, data);
+
+ nb_notification_send(xpath, arguments);
+}
+
+/*
+ * XPath: /frr-isisd:lsp-error-detected
+ */
+void isis_notif_lsp_error(const struct isis_circuit *circuit,
+ const char *lsp_id, const char *raw_pdu,
+ __attribute__((unused)) uint32_t offset,
+ __attribute__((unused)) uint8_t tlv_type)
+{
+ const char *xpath = "/frr-isisd:lsp-error-detected";
+ struct list *arguments = yang_data_list_new();
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+ struct isis_area *area = circuit->area;
+
+ notif_prep_instance_hdr(xpath, area, "default", arguments);
+ notif_prepr_iface_hdr(xpath, circuit, arguments);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
+ data = yang_data_new_string(xpath_arg, lsp_id);
+ listnode_add(arguments, data);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath);
+ data = yang_data_new(xpath_arg, raw_pdu);
+ listnode_add(arguments, data);
+ /* ignore offset and tlv_type which cannot be set properly */
+
+ nb_notification_send(xpath, arguments);
+}
+
+/*
+ * XPath: /frr-isisd:sequence-number-skipped
+ */
+void isis_notif_seqno_skipped(const struct isis_circuit *circuit,
+ const char *lsp_id)
+{
+ const char *xpath = "/frr-isisd:sequence-number-skipped";
+ struct list *arguments = yang_data_list_new();
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+ struct isis_area *area = circuit->area;
+
+ notif_prep_instance_hdr(xpath, area, "default", arguments);
+ notif_prepr_iface_hdr(xpath, circuit, arguments);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
+ data = yang_data_new_string(xpath_arg, lsp_id);
+ listnode_add(arguments, data);
+
+ nb_notification_send(xpath, arguments);
+}
+
+/*
+ * XPath: /frr-isisd:own-lsp-purge
+ */
+void isis_notif_own_lsp_purge(const struct isis_circuit *circuit,
+ const char *lsp_id)
+{
+ const char *xpath = "/frr-isisd:own-lsp-purge";
+ struct list *arguments = yang_data_list_new();
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+ struct isis_area *area = circuit->area;
+
+ notif_prep_instance_hdr(xpath, area, "default", arguments);
+ notif_prepr_iface_hdr(xpath, circuit, arguments);
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
+ data = yang_data_new_string(xpath_arg, lsp_id);
+ listnode_add(arguments, data);
+
+ nb_notification_send(xpath, arguments);
+}
diff --git a/isisd/isis_nb_state.c b/isisd/isis_nb_state.c
new file mode 100644
index 0000000000..e52d0201d5
--- /dev/null
+++ b/isisd/isis_nb_state.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2018 Volta Networks
+ * Emanuele Di Pascale
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "northbound.h"
+#include "linklist.h"
+
+#include "isisd/isisd.h"
+#include "isisd/isis_nb.h"
+#include "isisd/isis_circuit.h"
+#include "isisd/isis_adjacency.h"
+#include "isisd/isis_misc.h"
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency
+ */
+const void *
+lib_interface_isis_adjacencies_adjacency_get_next(const void *parent_list_entry,
+ const void *list_entry)
+{
+ struct interface *ifp;
+ struct isis_circuit *circuit;
+ struct isis_adjacency *adj, *adj_next = NULL;
+ struct list *list;
+ struct listnode *node, *node_next;
+
+ /* Get first adjacency. */
+ if (list_entry == NULL) {
+ ifp = (struct interface *)parent_list_entry;
+ if (!ifp)
+ return NULL;
+
+ circuit = circuit_scan_by_ifp(ifp);
+ if (!circuit)
+ return NULL;
+
+ switch (circuit->circ_type) {
+ case CIRCUIT_T_BROADCAST:
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS;
+ level++) {
+ adj = listnode_head(
+ circuit->u.bc.adjdb[level - 1]);
+ if (adj)
+ break;
+ }
+ break;
+ case CIRCUIT_T_P2P:
+ adj = circuit->u.p2p.neighbor;
+ break;
+ default:
+ adj = NULL;
+ break;
+ }
+
+ return adj;
+ }
+
+ /* Get next adjacency. */
+ adj = (struct isis_adjacency *)list_entry;
+ circuit = adj->circuit;
+ switch (circuit->circ_type) {
+ case CIRCUIT_T_BROADCAST:
+ list = circuit->u.bc.adjdb[adj->level - 1];
+ node = listnode_lookup(list, adj);
+ node_next = listnextnode(node);
+ if (node_next)
+ adj_next = listgetdata(node_next);
+ else if (adj->level == ISIS_LEVEL1) {
+ /*
+ * Once we finish the L1 adjacencies, move to the L2
+ * adjacencies list.
+ */
+ list = circuit->u.bc.adjdb[ISIS_LEVEL2 - 1];
+ adj_next = listnode_head(list);
+ }
+ break;
+ case CIRCUIT_T_P2P:
+ /* P2P circuits have at most one adjacency. */
+ default:
+ break;
+ }
+
+ return adj_next;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/neighbor-sys-type
+ */
+struct yang_data *
+lib_interface_isis_adjacencies_adjacency_neighbor_sys_type_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct isis_adjacency *adj = list_entry;
+
+ return yang_data_new_enum(xpath, adj->level);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/neighbor-sysid
+ */
+struct yang_data *
+lib_interface_isis_adjacencies_adjacency_neighbor_sysid_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct isis_adjacency *adj = list_entry;
+
+ return yang_data_new_string(xpath, sysid_print(adj->sysid));
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/neighbor-extended-circuit-id
+ */
+struct yang_data *
+lib_interface_isis_adjacencies_adjacency_neighbor_extended_circuit_id_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct isis_adjacency *adj = list_entry;
+
+ return yang_data_new_uint32(xpath, adj->circuit->circuit_id);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/neighbor-snpa
+ */
+struct yang_data *
+lib_interface_isis_adjacencies_adjacency_neighbor_snpa_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct isis_adjacency *adj = list_entry;
+
+ return yang_data_new_string(xpath, snpa_print(adj->snpa));
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/hold-timer
+ */
+struct yang_data *lib_interface_isis_adjacencies_adjacency_hold_timer_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct isis_adjacency *adj = list_entry;
+
+ return yang_data_new_uint16(xpath, adj->hold_time);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/neighbor-priority
+ */
+struct yang_data *
+lib_interface_isis_adjacencies_adjacency_neighbor_priority_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct isis_adjacency *adj = list_entry;
+
+ return yang_data_new_uint8(xpath, adj->prio[adj->level - 1]);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/state
+ */
+struct yang_data *
+lib_interface_isis_adjacencies_adjacency_state_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct isis_adjacency *adj = list_entry;
+
+ return yang_data_new_string(xpath, isis_adj_yang_state(adj->adj_state));
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/event-counters/adjacency-changes
+ */
+struct yang_data *lib_interface_isis_event_counters_adjacency_changes_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ struct interface *ifp;
+ struct isis_circuit *circuit;
+
+ ifp = (struct interface *)list_entry;
+ if (!ifp)
+ return NULL;
+
+ circuit = circuit_scan_by_ifp(ifp);
+ if (!circuit)
+ return NULL;
+
+ return yang_data_new_uint32(xpath, circuit->adj_state_changes);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/event-counters/adjacency-number
+ */
+struct yang_data *lib_interface_isis_event_counters_adjacency_number_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ struct interface *ifp;
+ struct isis_circuit *circuit;
+ struct isis_adjacency *adj;
+ struct listnode *node;
+ uint32_t total = 0;
+
+ ifp = (struct interface *)list_entry;
+ if (!ifp)
+ return NULL;
+
+ circuit = circuit_scan_by_ifp(ifp);
+ if (!circuit)
+ return NULL;
+
+ /*
+ * TODO: keep track of the number of adjacencies instead of calculating
+ * it on demand.
+ */
+ switch (circuit->circ_type) {
+ case CIRCUIT_T_BROADCAST:
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
+ for (ALL_LIST_ELEMENTS_RO(
+ circuit->u.bc.adjdb[level - 1], node, adj))
+ total++;
+ }
+ break;
+ case CIRCUIT_T_P2P:
+ adj = circuit->u.p2p.neighbor;
+ if (adj)
+ total = 1;
+ break;
+ default:
+ break;
+ }
+
+ return yang_data_new_uint32(xpath, total);
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/event-counters/init-fails
+ */
+struct yang_data *
+lib_interface_isis_event_counters_init_fails_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ struct interface *ifp;
+ struct isis_circuit *circuit;
+
+ ifp = (struct interface *)list_entry;
+ if (!ifp)
+ return NULL;
+
+ circuit = circuit_scan_by_ifp(ifp);
+ if (!circuit)
+ return NULL;
+
+ return yang_data_new_uint32(xpath, circuit->init_failures);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/event-counters/adjacency-rejects
+ */
+struct yang_data *lib_interface_isis_event_counters_adjacency_rejects_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ struct interface *ifp;
+ struct isis_circuit *circuit;
+
+ ifp = (struct interface *)list_entry;
+ if (!ifp)
+ return NULL;
+
+ circuit = circuit_scan_by_ifp(ifp);
+ if (!circuit)
+ return NULL;
+
+ return yang_data_new_uint32(xpath, circuit->rej_adjacencies);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/event-counters/id-len-mismatch
+ */
+struct yang_data *lib_interface_isis_event_counters_id_len_mismatch_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ struct interface *ifp;
+ struct isis_circuit *circuit;
+
+ ifp = (struct interface *)list_entry;
+ if (!ifp)
+ return NULL;
+
+ circuit = circuit_scan_by_ifp(ifp);
+ if (!circuit)
+ return NULL;
+
+ return yang_data_new_uint32(xpath, circuit->id_len_mismatches);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/event-counters/max-area-addresses-mismatch
+ */
+struct yang_data *
+lib_interface_isis_event_counters_max_area_addresses_mismatch_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ struct interface *ifp;
+ struct isis_circuit *circuit;
+
+ ifp = (struct interface *)list_entry;
+ if (!ifp)
+ return NULL;
+
+ circuit = circuit_scan_by_ifp(ifp);
+ if (!circuit)
+ return NULL;
+
+ return yang_data_new_uint32(xpath, circuit->max_area_addr_mismatches);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/event-counters/authentication-type-fails
+ */
+struct yang_data *
+lib_interface_isis_event_counters_authentication_type_fails_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ struct interface *ifp;
+ struct isis_circuit *circuit;
+
+ ifp = (struct interface *)list_entry;
+ if (!ifp)
+ return NULL;
+
+ circuit = circuit_scan_by_ifp(ifp);
+ if (!circuit)
+ return NULL;
+
+ return yang_data_new_uint32(xpath, circuit->auth_type_failures);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/event-counters/authentication-fails
+ */
+struct yang_data *
+lib_interface_isis_event_counters_authentication_fails_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ struct interface *ifp;
+ struct isis_circuit *circuit;
+
+ ifp = (struct interface *)list_entry;
+ if (!ifp)
+ return NULL;
+
+ circuit = circuit_scan_by_ifp(ifp);
+ if (!circuit)
+ return NULL;
+
+ return yang_data_new_uint32(xpath, circuit->auth_failures);
+}
diff --git a/isisd/isis_northbound.c b/isisd/isis_northbound.c
deleted file mode 100644
index 1e21efa7ce..0000000000
--- a/isisd/isis_northbound.c
+++ /dev/null
@@ -1,3953 +0,0 @@
-/*
- * Copyright (C) 2001,2002 Sampo Saaristo
- * Tampere University of Technology
- * Institute of Communications Engineering
- * Copyright (C) 2018 Volta Networks
- * Emanuele Di Pascale
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; see the file COPYING; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <zebra.h>
-#include "northbound.h"
-#include "libfrr.h"
-#include "linklist.h"
-#include "log.h"
-#include "lib/bfd.h"
-#include "isisd/isis_bfd.h"
-#include "isisd/isis_constants.h"
-#include "isisd/isis_common.h"
-#include "isisd/isis_flags.h"
-#include "isisd/isis_circuit.h"
-#include "isisd/isisd.h"
-#include "isisd/isis_lsp.h"
-#include "isisd/isis_pdu.h"
-#include "isisd/isis_dynhn.h"
-#include "isisd/isis_misc.h"
-#include "isisd/isis_csm.h"
-#include "isisd/isis_adjacency.h"
-#include "isisd/isis_spf.h"
-#include "isisd/isis_te.h"
-#include "isisd/isis_memory.h"
-#include "isisd/isis_mt.h"
-#include "isisd/isis_cli.h"
-#include "isisd/isis_redist.h"
-#include "lib/spf_backoff.h"
-#include "lib/lib_errors.h"
-#include "lib/vrf.h"
-
-/*
- * Helper functions.
- */
-static const char *isis_yang_adj_state(enum isis_adj_state state)
-{
- switch (state) {
- case ISIS_ADJ_DOWN:
- return "down";
- case ISIS_ADJ_UP:
- return "up";
- case ISIS_ADJ_INITIALIZING:
- return "init";
- default:
- return "failed";
- }
-}
-
-/*
- * XPath: /frr-isisd:isis/instance
- */
-static int isis_instance_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_area *area;
- const char *area_tag;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area_tag = yang_dnode_get_string(dnode, "./area-tag");
- area = isis_area_lookup(area_tag);
- if (area)
- return NB_ERR_INCONSISTENCY;
-
- area = isis_area_create(area_tag);
- /* save area in dnode to avoid looking it up all the time */
- nb_running_set_entry(dnode, area);
-
- return NB_OK;
-}
-
-static int isis_instance_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct isis_area *area;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_unset_entry(dnode);
- isis_area_destroy(area->area_tag);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/is-type
- */
-static int isis_instance_is_type_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_area *area;
- int type;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- type = yang_dnode_get_enum(dnode, NULL);
- isis_area_is_type_set(area, type);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/area-address
- */
-static int isis_instance_area_address_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_area *area;
- struct area_addr addr, *addrr = NULL, *addrp = NULL;
- struct listnode *node;
- uint8_t buff[255];
- const char *net_title = yang_dnode_get_string(dnode, NULL);
-
- switch (event) {
- case NB_EV_VALIDATE:
- addr.addr_len = dotformat2buff(buff, net_title);
- memcpy(addr.area_addr, buff, addr.addr_len);
- if (addr.area_addr[addr.addr_len - 1] != 0) {
- flog_warn(
- EC_LIB_NB_CB_CONFIG_VALIDATE,
- "nsel byte (last byte) in area address must be 0");
- return NB_ERR_VALIDATION;
- }
- if (isis->sysid_set) {
- /* Check that the SystemID portions match */
- if (memcmp(isis->sysid, GETSYSID((&addr)),
- ISIS_SYS_ID_LEN)) {
- flog_warn(
- EC_LIB_NB_CB_CONFIG_VALIDATE,
- "System ID must not change when defining additional area addresses");
- return NB_ERR_VALIDATION;
- }
- }
- break;
- case NB_EV_PREPARE:
- addrr = XMALLOC(MTYPE_ISIS_AREA_ADDR, sizeof(struct area_addr));
- addrr->addr_len = dotformat2buff(buff, net_title);
- memcpy(addrr->area_addr, buff, addrr->addr_len);
- resource->ptr = addrr;
- break;
- case NB_EV_ABORT:
- XFREE(MTYPE_ISIS_AREA_ADDR, resource->ptr);
- break;
- case NB_EV_APPLY:
- area = nb_running_get_entry(dnode, NULL, true);
- addrr = resource->ptr;
-
- if (isis->sysid_set == 0) {
- /*
- * First area address - get the SystemID for this router
- */
- memcpy(isis->sysid, GETSYSID(addrr), ISIS_SYS_ID_LEN);
- isis->sysid_set = 1;
- } else {
- /* check that we don't already have this address */
- for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node,
- addrp)) {
- if ((addrp->addr_len + ISIS_SYS_ID_LEN
- + ISIS_NSEL_LEN)
- != (addrr->addr_len))
- continue;
- if (!memcmp(addrp->area_addr, addrr->area_addr,
- addrr->addr_len)) {
- XFREE(MTYPE_ISIS_AREA_ADDR, addrr);
- return NB_OK; /* silent fail */
- }
- }
- }
-
- /*Forget the systemID part of the address */
- addrr->addr_len -= (ISIS_SYS_ID_LEN + ISIS_NSEL_LEN);
- assert(area->area_addrs); /* to silence scan-build sillyness */
- listnode_add(area->area_addrs, addrr);
-
- /* only now we can safely generate our LSPs for this area */
- if (listcount(area->area_addrs) > 0) {
- if (area->is_type & IS_LEVEL_1)
- lsp_generate(area, IS_LEVEL_1);
- if (area->is_type & IS_LEVEL_2)
- lsp_generate(area, IS_LEVEL_2);
- }
- break;
- }
-
- return NB_OK;
-}
-
-static int isis_instance_area_address_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct area_addr addr, *addrp = NULL;
- struct listnode *node;
- uint8_t buff[255];
- struct isis_area *area;
- const char *net_title;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- net_title = yang_dnode_get_string(dnode, NULL);
- addr.addr_len = dotformat2buff(buff, net_title);
- memcpy(addr.area_addr, buff, (int)addr.addr_len);
- area = nb_running_get_entry(dnode, NULL, true);
- for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node, addrp)) {
- if ((addrp->addr_len + ISIS_SYS_ID_LEN + 1) == addr.addr_len
- && !memcmp(addrp->area_addr, addr.area_addr, addr.addr_len))
- break;
- }
- if (!addrp)
- return NB_ERR_INCONSISTENCY;
-
- listnode_delete(area->area_addrs, addrp);
- XFREE(MTYPE_ISIS_AREA_ADDR, addrp);
- /*
- * Last area address - reset the SystemID for this router
- */
- if (listcount(area->area_addrs) == 0) {
- memset(isis->sysid, 0, ISIS_SYS_ID_LEN);
- isis->sysid_set = 0;
- if (isis->debugs & DEBUG_EVENTS)
- zlog_debug("Router has no SystemID");
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/dynamic-hostname
- */
-static int isis_instance_dynamic_hostname_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_area *area;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- isis_area_dynhostname_set(area, yang_dnode_get_bool(dnode, NULL));
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/attached
- */
-static int isis_instance_attached_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_area *area;
- bool attached;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- attached = yang_dnode_get_bool(dnode, NULL);
- isis_area_attached_bit_set(area, attached);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/overload
- */
-static int isis_instance_overload_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_area *area;
- bool overload;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- overload = yang_dnode_get_bool(dnode, NULL);
- isis_area_overload_bit_set(area, overload);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/metric-style
- */
-static int isis_instance_metric_style_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_area *area;
- bool old_metric, new_metric;
- enum isis_metric_style metric_style = yang_dnode_get_enum(dnode, NULL);
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- old_metric = (metric_style == ISIS_WIDE_METRIC) ? false : true;
- new_metric = (metric_style == ISIS_NARROW_METRIC) ? false : true;
- isis_area_metricstyle_set(area, old_metric, new_metric);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/purge-originator
- */
-static int isis_instance_purge_originator_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_area *area;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- area->purge_originator = yang_dnode_get_bool(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/lsp/mtu
- */
-static int isis_instance_lsp_mtu_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct listnode *node;
- struct isis_circuit *circuit;
- uint16_t lsp_mtu = yang_dnode_get_uint16(dnode, NULL);
- struct isis_area *area;
-
- switch (event) {
- case NB_EV_VALIDATE:
- area = nb_running_get_entry(dnode, NULL, false);
- if (!area)
- break;
- for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
- if (circuit->state != C_STATE_INIT
- && circuit->state != C_STATE_UP)
- continue;
- if (lsp_mtu > isis_circuit_pdu_size(circuit)) {
- flog_warn(
- EC_LIB_NB_CB_CONFIG_VALIDATE,
- "ISIS area contains circuit %s, which has a maximum PDU size of %zu",
- circuit->interface->name,
- isis_circuit_pdu_size(circuit));
- return NB_ERR_VALIDATION;
- }
- }
- break;
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- break;
- case NB_EV_APPLY:
- area = nb_running_get_entry(dnode, NULL, true);
- isis_area_lsp_mtu_set(area, lsp_mtu);
- break;
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/lsp/refresh-interval/level-1
- */
-static int
-isis_instance_lsp_refresh_interval_level_1_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_area *area;
- uint16_t refr_int;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- refr_int = yang_dnode_get_uint16(dnode, NULL);
- area = nb_running_get_entry(dnode, NULL, true);
- isis_area_lsp_refresh_set(area, IS_LEVEL_1, refr_int);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/lsp/refresh-interval/level-2
- */
-static int
-isis_instance_lsp_refresh_interval_level_2_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_area *area;
- uint16_t refr_int;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- refr_int = yang_dnode_get_uint16(dnode, NULL);
- area = nb_running_get_entry(dnode, NULL, true);
- isis_area_lsp_refresh_set(area, IS_LEVEL_2, refr_int);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/lsp/maximum-lifetime/level-1
- */
-static int
-isis_instance_lsp_maximum_lifetime_level_1_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_area *area;
- uint16_t max_lt;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- max_lt = yang_dnode_get_uint16(dnode, NULL);
- area = nb_running_get_entry(dnode, NULL, true);
- isis_area_max_lsp_lifetime_set(area, IS_LEVEL_1, max_lt);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/lsp/maximum-lifetime/level-2
- */
-static int
-isis_instance_lsp_maximum_lifetime_level_2_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_area *area;
- uint16_t max_lt;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- max_lt = yang_dnode_get_uint16(dnode, NULL);
- area = nb_running_get_entry(dnode, NULL, true);
- isis_area_max_lsp_lifetime_set(area, IS_LEVEL_2, max_lt);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/lsp/generation-interval/level-1
- */
-static int isis_instance_lsp_generation_interval_level_1_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_area *area;
- uint16_t gen_int;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- gen_int = yang_dnode_get_uint16(dnode, NULL);
- area = nb_running_get_entry(dnode, NULL, true);
- area->lsp_gen_interval[0] = gen_int;
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/lsp/generation-interval/level-2
- */
-static int isis_instance_lsp_generation_interval_level_2_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_area *area;
- uint16_t gen_int;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- gen_int = yang_dnode_get_uint16(dnode, NULL);
- area = nb_running_get_entry(dnode, NULL, true);
- area->lsp_gen_interval[1] = gen_int;
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay
- */
-static void ietf_backoff_delay_apply_finish(const struct lyd_node *dnode)
-{
- long init_delay = yang_dnode_get_uint16(dnode, "./init-delay");
- long short_delay = yang_dnode_get_uint16(dnode, "./short-delay");
- long long_delay = yang_dnode_get_uint16(dnode, "./long-delay");
- long holddown = yang_dnode_get_uint16(dnode, "./hold-down");
- long timetolearn = yang_dnode_get_uint16(dnode, "./time-to-learn");
- struct isis_area *area = nb_running_get_entry(dnode, NULL, true);
- size_t bufsiz = strlen(area->area_tag) + sizeof("IS-IS Lx");
- char *buf = XCALLOC(MTYPE_TMP, bufsiz);
-
- snprintf(buf, bufsiz, "IS-IS %s L1", area->area_tag);
- spf_backoff_free(area->spf_delay_ietf[0]);
- area->spf_delay_ietf[0] =
- spf_backoff_new(master, buf, init_delay, short_delay,
- long_delay, holddown, timetolearn);
-
- snprintf(buf, bufsiz, "IS-IS %s L2", area->area_tag);
- spf_backoff_free(area->spf_delay_ietf[1]);
- area->spf_delay_ietf[1] =
- spf_backoff_new(master, buf, init_delay, short_delay,
- long_delay, holddown, timetolearn);
-
- XFREE(MTYPE_TMP, buf);
-}
-
-static int
-isis_instance_spf_ietf_backoff_delay_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* All the work is done in the apply_finish */
- return NB_OK;
-}
-
-static int
-isis_instance_spf_ietf_backoff_delay_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct isis_area *area;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- spf_backoff_free(area->spf_delay_ietf[0]);
- spf_backoff_free(area->spf_delay_ietf[1]);
- area->spf_delay_ietf[0] = NULL;
- area->spf_delay_ietf[1] = NULL;
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay/init-delay
- */
-static int isis_instance_spf_ietf_backoff_delay_init_delay_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* All the work is done in the apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay/short-delay
- */
-static int isis_instance_spf_ietf_backoff_delay_short_delay_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* All the work is done in the apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay/long-delay
- */
-static int isis_instance_spf_ietf_backoff_delay_long_delay_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* All the work is done in the apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay/hold-down
- */
-static int isis_instance_spf_ietf_backoff_delay_hold_down_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* All the work is done in the apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay/time-to-learn
- */
-static int isis_instance_spf_ietf_backoff_delay_time_to_learn_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* All the work is done in the apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/spf/minimum-interval/level-1
- */
-static int
-isis_instance_spf_minimum_interval_level_1_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_area *area;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- area->min_spf_interval[0] = yang_dnode_get_uint16(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/spf/minimum-interval/level-2
- */
-static int
-isis_instance_spf_minimum_interval_level_2_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_area *area;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- area->min_spf_interval[1] = yang_dnode_get_uint16(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/area-password
- */
-static void area_password_apply_finish(const struct lyd_node *dnode)
-{
- const char *password = yang_dnode_get_string(dnode, "./password");
- struct isis_area *area = nb_running_get_entry(dnode, NULL, true);
- int pass_type = yang_dnode_get_enum(dnode, "./password-type");
- uint8_t snp_auth = yang_dnode_get_enum(dnode, "./authenticate-snp");
-
- switch (pass_type) {
- case ISIS_PASSWD_TYPE_CLEARTXT:
- isis_area_passwd_cleartext_set(area, IS_LEVEL_1, password,
- snp_auth);
- break;
- case ISIS_PASSWD_TYPE_HMAC_MD5:
- isis_area_passwd_hmac_md5_set(area, IS_LEVEL_1, password,
- snp_auth);
- break;
- }
-}
-
-static int isis_instance_area_password_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* actual setting is done in apply_finish */
- return NB_OK;
-}
-
-static int isis_instance_area_password_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct isis_area *area;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- isis_area_passwd_unset(area, IS_LEVEL_1);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/area-password/password
- */
-static int
-isis_instance_area_password_password_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* actual setting is done in apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/area-password/password-type
- */
-static int
-isis_instance_area_password_password_type_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* actual setting is done in apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/area-password/authenticate-snp
- */
-static int isis_instance_area_password_authenticate_snp_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* actual setting is done in apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/domain-password
- */
-static void domain_password_apply_finish(const struct lyd_node *dnode)
-{
- const char *password = yang_dnode_get_string(dnode, "./password");
- struct isis_area *area = nb_running_get_entry(dnode, NULL, true);
- int pass_type = yang_dnode_get_enum(dnode, "./password-type");
- uint8_t snp_auth = yang_dnode_get_enum(dnode, "./authenticate-snp");
-
- switch (pass_type) {
- case ISIS_PASSWD_TYPE_CLEARTXT:
- isis_area_passwd_cleartext_set(area, IS_LEVEL_2, password,
- snp_auth);
- break;
- case ISIS_PASSWD_TYPE_HMAC_MD5:
- isis_area_passwd_hmac_md5_set(area, IS_LEVEL_2, password,
- snp_auth);
- break;
- }
-}
-
-static int isis_instance_domain_password_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* actual setting is done in apply_finish */
- return NB_OK;
-}
-
-static int isis_instance_domain_password_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct isis_area *area;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- isis_area_passwd_unset(area, IS_LEVEL_2);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/domain-password/password
- */
-static int
-isis_instance_domain_password_password_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* actual setting is done in apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/domain-password/password-type
- */
-static int
-isis_instance_domain_password_password_type_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* actual setting is done in apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/domain-password/authenticate-snp
- */
-static int isis_instance_domain_password_authenticate_snp_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* actual setting is done in apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/default-information-originate/ipv4
- */
-static void default_info_origin_apply_finish(const struct lyd_node *dnode,
- int family)
-{
- int originate_type = DEFAULT_ORIGINATE;
- unsigned long metric = 0;
- const char *routemap = NULL;
- struct isis_area *area = nb_running_get_entry(dnode, NULL, true);
- int level = yang_dnode_get_enum(dnode, "./level");
-
- if (yang_dnode_get_bool(dnode, "./always")) {
- originate_type = DEFAULT_ORIGINATE_ALWAYS;
- } else if (family == AF_INET6) {
- zlog_warn(
- "%s: Zebra doesn't implement default-originate for IPv6 yet, so use with care or use default-originate always.",
- __func__);
- }
-
- if (yang_dnode_exists(dnode, "./metric"))
- metric = yang_dnode_get_uint32(dnode, "./metric");
- if (yang_dnode_exists(dnode, "./route-map"))
- routemap = yang_dnode_get_string(dnode, "./route-map");
-
- isis_redist_set(area, level, family, DEFAULT_ROUTE, metric, routemap,
- originate_type);
-}
-
-static void default_info_origin_ipv4_apply_finish(const struct lyd_node *dnode)
-{
- default_info_origin_apply_finish(dnode, AF_INET);
-}
-
-static void default_info_origin_ipv6_apply_finish(const struct lyd_node *dnode)
-{
- default_info_origin_apply_finish(dnode, AF_INET6);
-}
-
-static int isis_instance_default_information_originate_ipv4_create(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* It's all done by default_info_origin_apply_finish */
- return NB_OK;
-}
-
-static int isis_instance_default_information_originate_ipv4_destroy(
- enum nb_event event, const struct lyd_node *dnode)
-{
- struct isis_area *area;
- int level;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- level = yang_dnode_get_enum(dnode, "./level");
- isis_redist_unset(area, level, AF_INET, DEFAULT_ROUTE);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/default-information-originate/ipv4/always
- */
-static int isis_instance_default_information_originate_ipv4_always_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* It's all done by default_info_origin_apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/default-information-originate/ipv4/route-map
- */
-static int isis_instance_default_information_originate_ipv4_route_map_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* It's all done by default_info_origin_apply_finish */
- return NB_OK;
-}
-
-static int isis_instance_default_information_originate_ipv4_route_map_destroy(
- enum nb_event event, const struct lyd_node *dnode)
-{
- /* It's all done by default_info_origin_apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/default-information-originate/ipv4/metric
- */
-static int isis_instance_default_information_originate_ipv4_metric_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* It's all done by default_info_origin_apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/default-information-originate/ipv6
- */
-static int isis_instance_default_information_originate_ipv6_create(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* It's all done by default_info_origin_apply_finish */
- return NB_OK;
-}
-
-static int isis_instance_default_information_originate_ipv6_destroy(
- enum nb_event event, const struct lyd_node *dnode)
-{
- struct isis_area *area;
- int level;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- level = yang_dnode_get_enum(dnode, "./level");
- isis_redist_unset(area, level, AF_INET6, DEFAULT_ROUTE);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/default-information-originate/ipv6/always
- */
-static int isis_instance_default_information_originate_ipv6_always_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* It's all done by default_info_origin_apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/default-information-originate/ipv6/route-map
- */
-static int isis_instance_default_information_originate_ipv6_route_map_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* It's all done by default_info_origin_apply_finish */
- return NB_OK;
-}
-
-static int isis_instance_default_information_originate_ipv6_route_map_destroy(
- enum nb_event event, const struct lyd_node *dnode)
-{
- /* It's all done by default_info_origin_apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/default-information-originate/ipv6/metric
- */
-static int isis_instance_default_information_originate_ipv6_metric_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* It's all done by default_info_origin_apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/redistribute/ipv4
- */
-static void redistribute_apply_finish(const struct lyd_node *dnode, int family)
-{
- assert(family == AF_INET || family == AF_INET6);
- int type, level;
- unsigned long metric = 0;
- const char *routemap = NULL;
- struct isis_area *area;
-
- type = yang_dnode_get_enum(dnode, "./protocol");
- level = yang_dnode_get_enum(dnode, "./level");
- area = nb_running_get_entry(dnode, NULL, true);
-
- if (yang_dnode_exists(dnode, "./metric"))
- metric = yang_dnode_get_uint32(dnode, "./metric");
- if (yang_dnode_exists(dnode, "./route-map"))
- routemap = yang_dnode_get_string(dnode, "./route-map");
-
- isis_redist_set(area, level, family, type, metric, routemap, 0);
-}
-
-static void redistribute_ipv4_apply_finish(const struct lyd_node *dnode)
-{
- redistribute_apply_finish(dnode, AF_INET);
-}
-
-static void redistribute_ipv6_apply_finish(const struct lyd_node *dnode)
-{
- redistribute_apply_finish(dnode, AF_INET6);
-}
-
-static int isis_instance_redistribute_ipv4_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* It's all done by redistribute_apply_finish */
- return NB_OK;
-}
-
-static int isis_instance_redistribute_ipv4_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct isis_area *area;
- int level, type;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- level = yang_dnode_get_enum(dnode, "./level");
- type = yang_dnode_get_enum(dnode, "./protocol");
- isis_redist_unset(area, level, AF_INET, type);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/redistribute/ipv4/route-map
- */
-static int
-isis_instance_redistribute_ipv4_route_map_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* It's all done by redistribute_apply_finish */
- return NB_OK;
-}
-
-static int
-isis_instance_redistribute_ipv4_route_map_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- /* It's all done by redistribute_apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/redistribute/ipv4/metric
- */
-static int
-isis_instance_redistribute_ipv4_metric_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* It's all done by redistribute_apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/redistribute/ipv6
- */
-static int isis_instance_redistribute_ipv6_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* It's all done by redistribute_apply_finish */
- return NB_OK;
-}
-
-static int isis_instance_redistribute_ipv6_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct isis_area *area;
- int level, type;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- level = yang_dnode_get_enum(dnode, "./level");
- type = yang_dnode_get_enum(dnode, "./protocol");
- isis_redist_unset(area, level, AF_INET6, type);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/redistribute/ipv6/route-map
- */
-static int
-isis_instance_redistribute_ipv6_route_map_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* It's all done by redistribute_apply_finish */
- return NB_OK;
-}
-
-static int
-isis_instance_redistribute_ipv6_route_map_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- /* It's all done by redistribute_apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/redistribute/ipv6/metric
- */
-static int
-isis_instance_redistribute_ipv6_metric_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- /* It's all done by redistribute_apply_finish */
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/multi-topology/ipv4-multicast
- */
-static int isis_multi_topology_common(enum nb_event event,
- const struct lyd_node *dnode,
- const char *topology, bool create)
-{
- struct isis_area *area;
- struct isis_area_mt_setting *setting;
- uint16_t mtid = isis_str2mtid(topology);
-
- switch (event) {
- case NB_EV_VALIDATE:
- if (mtid == (uint16_t)-1) {
- flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
- "Unknown topology %s", topology);
- return NB_ERR_VALIDATION;
- }
- break;
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- break;
- case NB_EV_APPLY:
- area = nb_running_get_entry(dnode, NULL, true);
- setting = area_get_mt_setting(area, mtid);
- setting->enabled = create;
- lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 0);
- break;
- }
-
- return NB_OK;
-}
-
-static int isis_multi_topology_overload_common(enum nb_event event,
- const struct lyd_node *dnode,
- const char *topology)
-{
- struct isis_area *area;
- struct isis_area_mt_setting *setting;
- uint16_t mtid = isis_str2mtid(topology);
-
- /* validation is done in isis_multi_topology_common */
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- setting = area_get_mt_setting(area, mtid);
- setting->overload = yang_dnode_get_bool(dnode, NULL);
- if (setting->enabled)
- lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 0);
-
- return NB_OK;
-}
-
-static int
-isis_instance_multi_topology_ipv4_multicast_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return isis_multi_topology_common(event, dnode, "ipv4-multicast", true);
-}
-
-static int
-isis_instance_multi_topology_ipv4_multicast_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- return isis_multi_topology_common(event, dnode, "ipv4-multicast",
- false);
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/multi-topology/ipv4-multicast/overload
- */
-static int isis_instance_multi_topology_ipv4_multicast_overload_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return isis_multi_topology_overload_common(event, dnode,
- "ipv4-multicast");
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/multi-topology/ipv4-management
- */
-static int isis_instance_multi_topology_ipv4_management_create(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return isis_multi_topology_common(event, dnode, "ipv4-mgmt", true);
-}
-
-static int isis_instance_multi_topology_ipv4_management_destroy(
- enum nb_event event, const struct lyd_node *dnode)
-{
- return isis_multi_topology_common(event, dnode, "ipv4-mgmt", false);
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/multi-topology/ipv4-management/overload
- */
-static int isis_instance_multi_topology_ipv4_management_overload_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return isis_multi_topology_overload_common(event, dnode, "ipv4-mgmt");
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-unicast
- */
-static int
-isis_instance_multi_topology_ipv6_unicast_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return isis_multi_topology_common(event, dnode, "ipv6-unicast", true);
-}
-
-static int
-isis_instance_multi_topology_ipv6_unicast_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- return isis_multi_topology_common(event, dnode, "ipv6-unicast", false);
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-unicast/overload
- */
-static int isis_instance_multi_topology_ipv6_unicast_overload_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return isis_multi_topology_overload_common(event, dnode,
- "ipv6-unicast");
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-multicast
- */
-static int
-isis_instance_multi_topology_ipv6_multicast_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return isis_multi_topology_common(event, dnode, "ipv6-multicast", true);
-}
-
-static int
-isis_instance_multi_topology_ipv6_multicast_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- return isis_multi_topology_common(event, dnode, "ipv6-multicast",
- false);
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-multicast/overload
- */
-static int isis_instance_multi_topology_ipv6_multicast_overload_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return isis_multi_topology_overload_common(event, dnode,
- "ipv6-multicast");
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-management
- */
-static int isis_instance_multi_topology_ipv6_management_create(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return isis_multi_topology_common(event, dnode, "ipv6-mgmt", true);
-}
-
-static int isis_instance_multi_topology_ipv6_management_destroy(
- enum nb_event event, const struct lyd_node *dnode)
-{
- return isis_multi_topology_common(event, dnode, "ipv6-mgmt", false);
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-management/overload
- */
-static int isis_instance_multi_topology_ipv6_management_overload_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return isis_multi_topology_overload_common(event, dnode, "ipv6-mgmt");
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-dstsrc
- */
-static int
-isis_instance_multi_topology_ipv6_dstsrc_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return isis_multi_topology_common(event, dnode, "ipv6-dstsrc", true);
-}
-
-static int
-isis_instance_multi_topology_ipv6_dstsrc_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- return isis_multi_topology_common(event, dnode, "ipv6-dstsrc", false);
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-dstsrc/overload
- */
-static int isis_instance_multi_topology_ipv6_dstsrc_overload_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return isis_multi_topology_overload_common(event, dnode, "ipv6-dstsrc");
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/log-adjacency-changes
- */
-static int
-isis_instance_log_adjacency_changes_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_area *area;
- bool log = yang_dnode_get_bool(dnode, NULL);
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- area->log_adj_changes = log ? 1 : 0;
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/mpls-te
- */
-static int isis_instance_mpls_te_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct listnode *node;
- struct isis_area *area;
- struct isis_circuit *circuit;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- if (area->mta == NULL) {
-
- struct mpls_te_area *new;
-
- zlog_debug("ISIS-TE(%s): Initialize MPLS Traffic Engineering",
- area->area_tag);
-
- new = XCALLOC(MTYPE_ISIS_MPLS_TE, sizeof(struct mpls_te_area));
-
- /* Initialize MPLS_TE structure */
- new->status = enable;
- new->level = 0;
- new->inter_as = off;
- new->interas_areaid.s_addr = 0;
- new->router_id.s_addr = 0;
-
- area->mta = new;
- } else {
- area->mta->status = enable;
- }
-
- /*
- * Following code is intended to handle two cases;
- *
- * 1) MPLS-TE was disabled at startup time, but now become enabled.
- * In this case, we must enable MPLS-TE Circuit regarding interface
- * MPLS_TE flag
- * 2) MPLS-TE was once enabled then disabled, and now enabled again.
- */
- for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
- if (circuit->ext == NULL)
- continue;
-
- if (!IS_EXT_TE(circuit->ext)
- && HAS_LINK_PARAMS(circuit->interface))
- isis_link_params_update(circuit, circuit->interface);
- else
- continue;
-
- /* Reoriginate STD_TE & GMPLS circuits */
- if (circuit->area)
- lsp_regenerate_schedule(circuit->area, circuit->is_type,
- 0);
- }
-
- return NB_OK;
-}
-
-static int isis_instance_mpls_te_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct listnode *node;
- struct isis_area *area;
- struct isis_circuit *circuit;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- if (IS_MPLS_TE(area->mta))
- area->mta->status = disable;
- else
- return NB_OK;
-
- /* Flush LSP if circuit engage */
- for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
- if (!IS_EXT_TE(circuit->ext))
- continue;
-
- /* disable MPLS_TE Circuit keeping SR one's */
- if (IS_SUBTLV(circuit->ext, EXT_ADJ_SID))
- circuit->ext->status = EXT_ADJ_SID;
- else if (IS_SUBTLV(circuit->ext, EXT_LAN_ADJ_SID))
- circuit->ext->status = EXT_LAN_ADJ_SID;
- else
- circuit->ext->status = 0;
-
- /* Re-originate circuit without STD_TE & GMPLS parameters */
- if (circuit->area)
- lsp_regenerate_schedule(circuit->area, circuit->is_type,
- 0);
- }
-
- zlog_debug("ISIS-TE(%s): Disabled MPLS Traffic Engineering",
- area->area_tag);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-isisd:isis/instance/mpls-te/router-address
- */
-static int isis_instance_mpls_te_router_address_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct in_addr value;
- struct isis_area *area;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- /* only proceed if MPLS-TE is enabled */
- if (!IS_MPLS_TE(area->mta))
- return NB_OK;
-
- /* Update Area Router ID */
- yang_dnode_get_ipv4(&value, dnode, NULL);
- area->mta->router_id.s_addr = value.s_addr;
-
- /* And re-schedule LSP update */
- if (listcount(area->area_addrs) > 0)
- lsp_regenerate_schedule(area, area->is_type, 0);
-
- return NB_OK;
-}
-
-static int isis_instance_mpls_te_router_address_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct isis_area *area;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- area = nb_running_get_entry(dnode, NULL, true);
- /* only proceed if MPLS-TE is enabled */
- if (!IS_MPLS_TE(area->mta))
- return NB_OK;
-
- /* Reset Area Router ID */
- area->mta->router_id.s_addr = INADDR_ANY;
-
- /* And re-schedule LSP update */
- if (listcount(area->area_addrs) > 0)
- lsp_regenerate_schedule(area, area->is_type, 0);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis
- */
-static int lib_interface_isis_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_area *area;
- struct interface *ifp;
- struct isis_circuit *circuit;
- const char *area_tag = yang_dnode_get_string(dnode, "./area-tag");
- uint32_t min_mtu, actual_mtu;
-
- switch (event) {
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- break;
- case NB_EV_VALIDATE:
- /* check if interface mtu is sufficient. If the area has not
- * been created yet, assume default MTU for the area
- */
- ifp = nb_running_get_entry(dnode, NULL, false);
- /* zebra might not know yet about the MTU - nothing we can do */
- if (!ifp || ifp->mtu == 0)
- break;
- actual_mtu =
- if_is_broadcast(ifp) ? ifp->mtu - LLC_LEN : ifp->mtu;
- area = isis_area_lookup(area_tag);
- if (area)
- min_mtu = area->lsp_mtu;
- else
-#ifndef FABRICD
- min_mtu = yang_get_default_uint16(
- "/frr-isisd:isis/instance/lsp/mtu");
-#else
- min_mtu = DEFAULT_LSP_MTU;
-#endif /* ifndef FABRICD */
- if (actual_mtu < min_mtu) {
- flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
- "Interface %s has MTU %" PRIu32
- ", minimum MTU for the area is %" PRIu32 "",
- ifp->name, actual_mtu, min_mtu);
- return NB_ERR_VALIDATION;
- }
- break;
- case NB_EV_APPLY:
- area = isis_area_lookup(area_tag);
- /* The area should have already be created. We are
- * setting the priority of the global isis area creation
- * slightly lower, so it should be executed first, but I
- * cannot rely on that so here I have to check.
- */
- if (!area) {
- flog_err(
- EC_LIB_NB_CB_CONFIG_APPLY,
- "%s: attempt to create circuit for area %s before the area has been created",
- __func__, area_tag);
- abort();
- }
-
- ifp = nb_running_get_entry(dnode, NULL, true);
- circuit = isis_circuit_create(area, ifp);
- assert(circuit
- && (circuit->state == C_STATE_CONF
- || circuit->state == C_STATE_UP));
- nb_running_set_entry(dnode, circuit);
- break;
- }
-
- return NB_OK;
-}
-
-static int lib_interface_isis_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct isis_circuit *circuit;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- circuit = nb_running_unset_entry(dnode);
- if (!circuit)
- return NB_ERR_INCONSISTENCY;
- if (circuit->state == C_STATE_UP || circuit->state == C_STATE_CONF)
- isis_csm_state_change(ISIS_DISABLE, circuit, circuit->area);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/area-tag
- */
-static int lib_interface_isis_area_tag_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_circuit *circuit;
- struct interface *ifp;
- struct vrf *vrf;
- const char *area_tag, *ifname, *vrfname;
-
- if (event == NB_EV_VALIDATE) {
- /* libyang doesn't like relative paths across module boundaries
- */
- ifname = yang_dnode_get_string(dnode->parent->parent, "./name");
- vrfname = yang_dnode_get_string(dnode->parent->parent, "./vrf");
- vrf = vrf_lookup_by_name(vrfname);
- assert(vrf);
- ifp = if_lookup_by_name(ifname, vrf->vrf_id);
- if (!ifp)
- return NB_OK;
- circuit = circuit_lookup_by_ifp(ifp, isis->init_circ_list);
- area_tag = yang_dnode_get_string(dnode, NULL);
- if (circuit && circuit->area && circuit->area->area_tag
- && strcmp(circuit->area->area_tag, area_tag)) {
- flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
- "ISIS circuit is already defined on %s",
- circuit->area->area_tag);
- return NB_ERR_VALIDATION;
- }
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/circuit-type
- */
-static int lib_interface_isis_circuit_type_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- int circ_type = yang_dnode_get_enum(dnode, NULL);
- struct isis_circuit *circuit;
- struct interface *ifp;
- struct vrf *vrf;
- const char *ifname, *vrfname;
-
- switch (event) {
- case NB_EV_VALIDATE:
- /* libyang doesn't like relative paths across module boundaries
- */
- ifname = yang_dnode_get_string(dnode->parent->parent, "./name");
- vrfname = yang_dnode_get_string(dnode->parent->parent, "./vrf");
- vrf = vrf_lookup_by_name(vrfname);
- assert(vrf);
- ifp = if_lookup_by_name(ifname, vrf->vrf_id);
- if (!ifp)
- break;
- circuit = circuit_lookup_by_ifp(ifp, isis->init_circ_list);
- if (circuit && circuit->state == C_STATE_UP
- && circuit->area->is_type != IS_LEVEL_1_AND_2
- && circuit->area->is_type != circ_type) {
- flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
- "Invalid circuit level for area %s",
- circuit->area->area_tag);
- return NB_ERR_VALIDATION;
- }
- break;
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- break;
- case NB_EV_APPLY:
- circuit = nb_running_get_entry(dnode, NULL, true);
- isis_circuit_is_type_set(circuit, circ_type);
- break;
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/ipv4-routing
- */
-static int lib_interface_isis_ipv4_routing_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- bool ipv4, ipv6;
- struct isis_circuit *circuit;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- circuit = nb_running_get_entry(dnode, NULL, true);
- ipv4 = yang_dnode_get_bool(dnode, NULL);
- ipv6 = yang_dnode_get_bool(dnode, "../ipv6-routing");
- isis_circuit_af_set(circuit, ipv4, ipv6);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/ipv6-routing
- */
-static int lib_interface_isis_ipv6_routing_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- bool ipv4, ipv6;
- struct isis_circuit *circuit;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- circuit = nb_running_get_entry(dnode, NULL, true);
- ipv4 = yang_dnode_exists(dnode, "../ipv4-routing");
- ipv6 = yang_dnode_get_bool(dnode, NULL);
- isis_circuit_af_set(circuit, ipv4, ipv6);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring
- */
-static int lib_interface_isis_bfd_monitoring_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_circuit *circuit;
- bool bfd_monitoring;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- circuit = nb_running_get_entry(dnode, NULL, true);
- bfd_monitoring = yang_dnode_get_bool(dnode, NULL);
-
- if (bfd_monitoring) {
- isis_bfd_circuit_param_set(circuit, BFD_DEF_MIN_RX,
- BFD_DEF_MIN_TX, BFD_DEF_DETECT_MULT,
- true);
- } else {
- isis_bfd_circuit_cmd(circuit, ZEBRA_BFD_DEST_DEREGISTER);
- bfd_info_free(&circuit->bfd_info);
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/csnp-interval/level-1
- */
-static int
-lib_interface_isis_csnp_interval_level_1_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_circuit *circuit;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- circuit = nb_running_get_entry(dnode, NULL, true);
- circuit->csnp_interval[0] = yang_dnode_get_uint16(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/csnp-interval/level-2
- */
-static int
-lib_interface_isis_csnp_interval_level_2_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_circuit *circuit;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- circuit = nb_running_get_entry(dnode, NULL, true);
- circuit->csnp_interval[1] = yang_dnode_get_uint16(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/psnp-interval/level-1
- */
-static int
-lib_interface_isis_psnp_interval_level_1_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_circuit *circuit;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- circuit = nb_running_get_entry(dnode, NULL, true);
- circuit->psnp_interval[0] = yang_dnode_get_uint16(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/psnp-interval/level-2
- */
-static int
-lib_interface_isis_psnp_interval_level_2_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_circuit *circuit;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- circuit = nb_running_get_entry(dnode, NULL, true);
- circuit->psnp_interval[1] = yang_dnode_get_uint16(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/padding
- */
-static int lib_interface_isis_hello_padding_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_circuit *circuit;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- circuit = nb_running_get_entry(dnode, NULL, true);
- circuit->pad_hellos = yang_dnode_get_bool(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-1
- */
-static int
-lib_interface_isis_hello_interval_level_1_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_circuit *circuit;
- uint32_t interval;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- circuit = nb_running_get_entry(dnode, NULL, true);
- interval = yang_dnode_get_uint32(dnode, NULL);
- circuit->hello_interval[0] = interval;
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-2
- */
-static int
-lib_interface_isis_hello_interval_level_2_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_circuit *circuit;
- uint32_t interval;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- circuit = nb_running_get_entry(dnode, NULL, true);
- interval = yang_dnode_get_uint32(dnode, NULL);
- circuit->hello_interval[1] = interval;
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/multiplier/level-1
- */
-static int
-lib_interface_isis_hello_multiplier_level_1_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_circuit *circuit;
- uint16_t multi;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- circuit = nb_running_get_entry(dnode, NULL, true);
- multi = yang_dnode_get_uint16(dnode, NULL);
- circuit->hello_multiplier[0] = multi;
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/multiplier/level-2
- */
-static int
-lib_interface_isis_hello_multiplier_level_2_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_circuit *circuit;
- uint16_t multi;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- circuit = nb_running_get_entry(dnode, NULL, true);
- multi = yang_dnode_get_uint16(dnode, NULL);
- circuit->hello_multiplier[1] = multi;
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/metric/level-1
- */
-static int
-lib_interface_isis_metric_level_1_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_circuit *circuit;
- unsigned int met;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- circuit = nb_running_get_entry(dnode, NULL, true);
- met = yang_dnode_get_uint32(dnode, NULL);
- isis_circuit_metric_set(circuit, IS_LEVEL_1, met);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/metric/level-2
- */
-static int
-lib_interface_isis_metric_level_2_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_circuit *circuit;
- unsigned int met;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- circuit = nb_running_get_entry(dnode, NULL, true);
- met = yang_dnode_get_uint32(dnode, NULL);
- isis_circuit_metric_set(circuit, IS_LEVEL_2, met);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/priority/level-1
- */
-static int
-lib_interface_isis_priority_level_1_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_circuit *circuit;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- circuit = nb_running_get_entry(dnode, NULL, true);
- circuit->priority[0] = yang_dnode_get_uint8(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/priority/level-2
- */
-static int
-lib_interface_isis_priority_level_2_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_circuit *circuit;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- circuit = nb_running_get_entry(dnode, NULL, true);
- circuit->priority[1] = yang_dnode_get_uint8(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/network-type
- */
-static int lib_interface_isis_network_type_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_circuit *circuit;
- int net_type = yang_dnode_get_enum(dnode, NULL);
-
- switch (event) {
- case NB_EV_VALIDATE:
- circuit = nb_running_get_entry(dnode, NULL, false);
- if (!circuit)
- break;
- if (circuit->circ_type == CIRCUIT_T_LOOPBACK) {
- flog_warn(
- EC_LIB_NB_CB_CONFIG_VALIDATE,
- "Cannot change network type on loopback interface");
- return NB_ERR_VALIDATION;
- }
- if (net_type == CIRCUIT_T_BROADCAST
- && circuit->state == C_STATE_UP
- && !if_is_broadcast(circuit->interface)) {
- flog_warn(
- EC_LIB_NB_CB_CONFIG_VALIDATE,
- "Cannot configure non-broadcast interface for broadcast operation");
- return NB_ERR_VALIDATION;
- }
- break;
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- break;
- case NB_EV_APPLY:
- circuit = nb_running_get_entry(dnode, NULL, true);
- isis_circuit_circ_type_set(circuit, net_type);
- break;
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/passive
- */
-static int lib_interface_isis_passive_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_circuit *circuit;
- struct isis_area *area;
- struct interface *ifp;
- bool passive = yang_dnode_get_bool(dnode, NULL);
-
- /* validation only applies if we are setting passive to false */
- if (!passive && event == NB_EV_VALIDATE) {
- circuit = nb_running_get_entry(dnode, NULL, false);
- if (!circuit)
- return NB_OK;
- ifp = circuit->interface;
- if (!ifp)
- return NB_OK;
- if (if_is_loopback(ifp)) {
- flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
- "Loopback is always passive");
- return NB_ERR_VALIDATION;
- }
- }
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- circuit = nb_running_get_entry(dnode, NULL, true);
- if (circuit->state != C_STATE_UP) {
- circuit->is_passive = passive;
- } else {
- area = circuit->area;
- isis_csm_state_change(ISIS_DISABLE, circuit, area);
- circuit->is_passive = passive;
- isis_csm_state_change(ISIS_ENABLE, circuit, area);
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/password
- */
-static int lib_interface_isis_password_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return NB_OK;
-}
-
-static int lib_interface_isis_password_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct isis_circuit *circuit;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- circuit = nb_running_get_entry(dnode, NULL, true);
- isis_circuit_passwd_unset(circuit);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/password/password
- */
-static int
-lib_interface_isis_password_password_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_circuit *circuit;
- const char *password;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- password = yang_dnode_get_string(dnode, NULL);
- circuit = nb_running_get_entry(dnode, NULL, true);
-
- isis_circuit_passwd_set(circuit, circuit->passwd.type, password);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/password/password-type
- */
-static int
-lib_interface_isis_password_password_type_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_circuit *circuit;
- uint8_t pass_type;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- pass_type = yang_dnode_get_enum(dnode, NULL);
- circuit = nb_running_get_entry(dnode, NULL, true);
- circuit->passwd.type = pass_type;
-
- return NB_OK;
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/disable-three-way-handshake
- */
-static int lib_interface_isis_disable_three_way_handshake_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct isis_circuit *circuit;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- circuit = nb_running_get_entry(dnode, NULL, true);
- circuit->disable_threeway_adj = yang_dnode_get_bool(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-unicast
- */
-static int lib_interface_isis_multi_topology_common(
- enum nb_event event, const struct lyd_node *dnode, uint16_t mtid)
-{
- struct isis_circuit *circuit;
- bool value;
-
- switch (event) {
- case NB_EV_VALIDATE:
- circuit = nb_running_get_entry(dnode, NULL, false);
- if (circuit && circuit->area && circuit->area->oldmetric) {
- flog_warn(
- EC_LIB_NB_CB_CONFIG_VALIDATE,
- "Multi topology IS-IS can only be used with wide metrics");
- return NB_ERR_VALIDATION;
- }
- break;
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- break;
- case NB_EV_APPLY:
- circuit = nb_running_get_entry(dnode, NULL, true);
- value = yang_dnode_get_bool(dnode, NULL);
- isis_circuit_mt_enabled_set(circuit, mtid, value);
- break;
- }
-
- return NB_OK;
-}
-
-static int lib_interface_isis_multi_topology_ipv4_unicast_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return lib_interface_isis_multi_topology_common(event, dnode,
- ISIS_MT_IPV4_UNICAST);
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-multicast
- */
-static int lib_interface_isis_multi_topology_ipv4_multicast_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return lib_interface_isis_multi_topology_common(event, dnode,
- ISIS_MT_IPV4_MULTICAST);
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-management
- */
-static int lib_interface_isis_multi_topology_ipv4_management_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return lib_interface_isis_multi_topology_common(event, dnode,
- ISIS_MT_IPV4_MGMT);
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-unicast
- */
-static int lib_interface_isis_multi_topology_ipv6_unicast_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return lib_interface_isis_multi_topology_common(event, dnode,
- ISIS_MT_IPV6_UNICAST);
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-multicast
- */
-static int lib_interface_isis_multi_topology_ipv6_multicast_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return lib_interface_isis_multi_topology_common(event, dnode,
- ISIS_MT_IPV6_MULTICAST);
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-management
- */
-static int lib_interface_isis_multi_topology_ipv6_management_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return lib_interface_isis_multi_topology_common(event, dnode,
- ISIS_MT_IPV6_MGMT);
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-dstsrc
- */
-static int lib_interface_isis_multi_topology_ipv6_dstsrc_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- return lib_interface_isis_multi_topology_common(event, dnode,
- ISIS_MT_IPV6_DSTSRC);
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency
- */
-static const void *
-lib_interface_isis_adjacencies_adjacency_get_next(const void *parent_list_entry,
- const void *list_entry)
-{
- struct interface *ifp;
- struct isis_circuit *circuit;
- struct isis_adjacency *adj, *adj_next = NULL;
- struct list *list;
- struct listnode *node, *node_next;
-
- /* Get first adjacency. */
- if (list_entry == NULL) {
- ifp = (struct interface *)parent_list_entry;
- if (!ifp)
- return NULL;
-
- circuit = circuit_scan_by_ifp(ifp);
- if (!circuit)
- return NULL;
-
- switch (circuit->circ_type) {
- case CIRCUIT_T_BROADCAST:
- for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS;
- level++) {
- adj = listnode_head(
- circuit->u.bc.adjdb[level - 1]);
- if (adj)
- break;
- }
- break;
- case CIRCUIT_T_P2P:
- adj = circuit->u.p2p.neighbor;
- break;
- default:
- adj = NULL;
- break;
- }
-
- return adj;
- }
-
- /* Get next adjacency. */
- adj = (struct isis_adjacency *)list_entry;
- circuit = adj->circuit;
- switch (circuit->circ_type) {
- case CIRCUIT_T_BROADCAST:
- list = circuit->u.bc.adjdb[adj->level - 1];
- node = listnode_lookup(list, adj);
- node_next = listnextnode(node);
- if (node_next)
- adj_next = listgetdata(node_next);
- else if (adj->level == ISIS_LEVEL1) {
- /*
- * Once we finish the L1 adjacencies, move to the L2
- * adjacencies list.
- */
- list = circuit->u.bc.adjdb[ISIS_LEVEL2 - 1];
- adj_next = listnode_head(list);
- }
- break;
- case CIRCUIT_T_P2P:
- /* P2P circuits have at most one adjacency. */
- default:
- break;
- }
-
- return adj_next;
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/neighbor-sys-type
- */
-static struct yang_data *
-lib_interface_isis_adjacencies_adjacency_neighbor_sys_type_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct isis_adjacency *adj = list_entry;
-
- return yang_data_new_enum(xpath, adj->level);
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/neighbor-sysid
- */
-static struct yang_data *
-lib_interface_isis_adjacencies_adjacency_neighbor_sysid_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct isis_adjacency *adj = list_entry;
-
- return yang_data_new_string(xpath, sysid_print(adj->sysid));
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/neighbor-extended-circuit-id
- */
-static struct yang_data *
-lib_interface_isis_adjacencies_adjacency_neighbor_extended_circuit_id_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct isis_adjacency *adj = list_entry;
-
- return yang_data_new_uint32(xpath, adj->circuit->circuit_id);
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/neighbor-snpa
- */
-static struct yang_data *
-lib_interface_isis_adjacencies_adjacency_neighbor_snpa_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct isis_adjacency *adj = list_entry;
-
- return yang_data_new_string(xpath, snpa_print(adj->snpa));
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/hold-timer
- */
-static struct yang_data *
-lib_interface_isis_adjacencies_adjacency_hold_timer_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct isis_adjacency *adj = list_entry;
-
- return yang_data_new_uint16(xpath, adj->hold_time);
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/neighbor-priority
- */
-static struct yang_data *
-lib_interface_isis_adjacencies_adjacency_neighbor_priority_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct isis_adjacency *adj = list_entry;
-
- return yang_data_new_uint8(xpath, adj->prio[adj->level - 1]);
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/state
- */
-static struct yang_data *
-lib_interface_isis_adjacencies_adjacency_state_get_elem(const char *xpath,
- const void *list_entry)
-{
- const struct isis_adjacency *adj = list_entry;
-
- return yang_data_new_string(xpath, isis_yang_adj_state(adj->adj_state));
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/event-counters/adjacency-changes
- */
-static struct yang_data *
-lib_interface_isis_event_counters_adjacency_changes_get_elem(
- const char *xpath, const void *list_entry)
-{
- struct interface *ifp;
- struct isis_circuit *circuit;
-
- ifp = (struct interface *)list_entry;
- if (!ifp)
- return NULL;
-
- circuit = circuit_scan_by_ifp(ifp);
- if (!circuit)
- return NULL;
-
- return yang_data_new_uint32(xpath, circuit->adj_state_changes);
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/event-counters/adjacency-number
- */
-static struct yang_data *
-lib_interface_isis_event_counters_adjacency_number_get_elem(
- const char *xpath, const void *list_entry)
-{
- struct interface *ifp;
- struct isis_circuit *circuit;
- struct isis_adjacency *adj;
- struct listnode *node;
- uint32_t total = 0;
-
- ifp = (struct interface *)list_entry;
- if (!ifp)
- return NULL;
-
- circuit = circuit_scan_by_ifp(ifp);
- if (!circuit)
- return NULL;
-
- /*
- * TODO: keep track of the number of adjacencies instead of calculating
- * it on demand.
- */
- switch (circuit->circ_type) {
- case CIRCUIT_T_BROADCAST:
- for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
- for (ALL_LIST_ELEMENTS_RO(
- circuit->u.bc.adjdb[level - 1], node, adj))
- total++;
- }
- break;
- case CIRCUIT_T_P2P:
- adj = circuit->u.p2p.neighbor;
- if (adj)
- total = 1;
- break;
- default:
- break;
- }
-
- return yang_data_new_uint32(xpath, total);
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/event-counters/init-fails
- */
-static struct yang_data *
-lib_interface_isis_event_counters_init_fails_get_elem(const char *xpath,
- const void *list_entry)
-{
- struct interface *ifp;
- struct isis_circuit *circuit;
-
- ifp = (struct interface *)list_entry;
- if (!ifp)
- return NULL;
-
- circuit = circuit_scan_by_ifp(ifp);
- if (!circuit)
- return NULL;
-
- return yang_data_new_uint32(xpath, circuit->init_failures);
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/event-counters/adjacency-rejects
- */
-static struct yang_data *
-lib_interface_isis_event_counters_adjacency_rejects_get_elem(
- const char *xpath, const void *list_entry)
-{
- struct interface *ifp;
- struct isis_circuit *circuit;
-
- ifp = (struct interface *)list_entry;
- if (!ifp)
- return NULL;
-
- circuit = circuit_scan_by_ifp(ifp);
- if (!circuit)
- return NULL;
-
- return yang_data_new_uint32(xpath, circuit->rej_adjacencies);
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/event-counters/id-len-mismatch
- */
-static struct yang_data *
-lib_interface_isis_event_counters_id_len_mismatch_get_elem(
- const char *xpath, const void *list_entry)
-{
- struct interface *ifp;
- struct isis_circuit *circuit;
-
- ifp = (struct interface *)list_entry;
- if (!ifp)
- return NULL;
-
- circuit = circuit_scan_by_ifp(ifp);
- if (!circuit)
- return NULL;
-
- return yang_data_new_uint32(xpath, circuit->id_len_mismatches);
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/event-counters/max-area-addresses-mismatch
- */
-static struct yang_data *
-lib_interface_isis_event_counters_max_area_addresses_mismatch_get_elem(
- const char *xpath, const void *list_entry)
-{
- struct interface *ifp;
- struct isis_circuit *circuit;
-
- ifp = (struct interface *)list_entry;
- if (!ifp)
- return NULL;
-
- circuit = circuit_scan_by_ifp(ifp);
- if (!circuit)
- return NULL;
-
- return yang_data_new_uint32(xpath, circuit->max_area_addr_mismatches);
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/event-counters/authentication-type-fails
- */
-static struct yang_data *
-lib_interface_isis_event_counters_authentication_type_fails_get_elem(
- const char *xpath, const void *list_entry)
-{
- struct interface *ifp;
- struct isis_circuit *circuit;
-
- ifp = (struct interface *)list_entry;
- if (!ifp)
- return NULL;
-
- circuit = circuit_scan_by_ifp(ifp);
- if (!circuit)
- return NULL;
-
- return yang_data_new_uint32(xpath, circuit->auth_type_failures);
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-isisd:isis/event-counters/authentication-fails
- */
-static struct yang_data *
-lib_interface_isis_event_counters_authentication_fails_get_elem(
- const char *xpath, const void *list_entry)
-{
- struct interface *ifp;
- struct isis_circuit *circuit;
-
- ifp = (struct interface *)list_entry;
- if (!ifp)
- return NULL;
-
- circuit = circuit_scan_by_ifp(ifp);
- if (!circuit)
- return NULL;
-
- return yang_data_new_uint32(xpath, circuit->auth_failures);
-}
-
-/*
- * NOTIFICATIONS
- */
-static void notif_prep_instance_hdr(const char *xpath,
- const struct isis_area *area,
- const char *routing_instance,
- struct list *args)
-{
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
-
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/routing-instance", xpath);
- data = yang_data_new_string(xpath_arg, routing_instance);
- listnode_add(args, data);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/routing-protocol-name",
- xpath);
- data = yang_data_new_string(xpath_arg, area->area_tag);
- listnode_add(args, data);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/isis-level", xpath);
- data = yang_data_new_enum(xpath_arg, area->is_type);
- listnode_add(args, data);
-}
-
-static void notif_prepr_iface_hdr(const char *xpath,
- const struct isis_circuit *circuit,
- struct list *args)
-{
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
-
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/interface-name", xpath);
- data = yang_data_new_string(xpath_arg, circuit->interface->name);
- listnode_add(args, data);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/interface-level", xpath);
- data = yang_data_new_enum(xpath_arg, circuit->is_type);
- listnode_add(args, data);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/extended-circuit-id", xpath);
- /* we do not seem to have the extended version of the circuit_id */
- data = yang_data_new_uint32(xpath_arg, (uint32_t)circuit->circuit_id);
- listnode_add(args, data);
-}
-
-/*
- * XPath:
- * /frr-isisd:database-overload
- */
-void isis_notif_db_overload(const struct isis_area *area, bool overload)
-{
- const char *xpath = "/frr-isisd:database-overload";
- struct list *arguments = yang_data_list_new();
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
-
- notif_prep_instance_hdr(xpath, area, "default", arguments);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/overload", xpath);
- data = yang_data_new_enum(xpath_arg, !!overload);
- listnode_add(arguments, data);
-
- nb_notification_send(xpath, arguments);
-}
-
-/*
- * XPath:
- * /frr-isisd:lsp-too-large
- */
-void isis_notif_lsp_too_large(const struct isis_circuit *circuit,
- uint32_t pdu_size, const char *lsp_id)
-{
- const char *xpath = "/frr-isisd:lsp-too-large";
- struct list *arguments = yang_data_list_new();
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
- struct isis_area *area = circuit->area;
-
- notif_prep_instance_hdr(xpath, area, "default", arguments);
- notif_prepr_iface_hdr(xpath, circuit, arguments);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/pdu-size", xpath);
- data = yang_data_new_uint32(xpath_arg, pdu_size);
- listnode_add(arguments, data);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
- data = yang_data_new_string(xpath_arg, lsp_id);
- listnode_add(arguments, data);
-
- nb_notification_send(xpath, arguments);
-}
-
-/*
- * XPath:
- * /frr-isisd:if-state-change
- */
-void isis_notif_if_state_change(const struct isis_circuit *circuit, bool down)
-{
- const char *xpath = "/frr-isisd:if-state-change";
- struct list *arguments = yang_data_list_new();
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
- struct isis_area *area = circuit->area;
-
- notif_prep_instance_hdr(xpath, area, "default", arguments);
- notif_prepr_iface_hdr(xpath, circuit, arguments);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/state", xpath);
- data = yang_data_new_enum(xpath_arg, !!down);
- listnode_add(arguments, data);
-
- nb_notification_send(xpath, arguments);
-}
-
-/*
- * XPath:
- * /frr-isisd:corrupted-lsp-detected
- */
-void isis_notif_corrupted_lsp(const struct isis_area *area, const char *lsp_id)
-{
- const char *xpath = "/frr-isisd:corrupted-lsp-detected";
- struct list *arguments = yang_data_list_new();
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
-
- notif_prep_instance_hdr(xpath, area, "default", arguments);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
- data = yang_data_new_string(xpath_arg, lsp_id);
- listnode_add(arguments, data);
-
- nb_notification_send(xpath, arguments);
-}
-
-/*
- * XPath:
- * /frr-isisd:attempt-to-exceed-max-sequence
- */
-void isis_notif_lsp_exceed_max(const struct isis_area *area, const char *lsp_id)
-{
- const char *xpath = "/frr-isisd:attempt-to-exceed-max-sequence";
- struct list *arguments = yang_data_list_new();
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
-
- notif_prep_instance_hdr(xpath, area, "default", arguments);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
- data = yang_data_new_string(xpath_arg, lsp_id);
- listnode_add(arguments, data);
-
- nb_notification_send(xpath, arguments);
-}
-
-/*
- * XPath:
- * /frr-isisd:max-area-addresses-mismatch
- */
-void isis_notif_max_area_addr_mismatch(const struct isis_circuit *circuit,
- uint8_t max_area_addrs,
- const char *raw_pdu)
-{
- const char *xpath = "/frr-isisd:max-area-addresses-mismatch";
- struct list *arguments = yang_data_list_new();
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
- struct isis_area *area = circuit->area;
-
- notif_prep_instance_hdr(xpath, area, "default", arguments);
- notif_prepr_iface_hdr(xpath, circuit, arguments);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/max-area-addresses", xpath);
- data = yang_data_new_uint8(xpath_arg, max_area_addrs);
- listnode_add(arguments, data);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath);
- data = yang_data_new(xpath_arg, raw_pdu);
- listnode_add(arguments, data);
-
- nb_notification_send(xpath, arguments);
-}
-
-/*
- * XPath:
- * /frr-isisd:authentication-type-failure
- */
-void isis_notif_authentication_type_failure(const struct isis_circuit *circuit,
- const char *raw_pdu)
-{
- const char *xpath = "/frr-isisd:authentication-type-failure";
- struct list *arguments = yang_data_list_new();
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
- struct isis_area *area = circuit->area;
-
- notif_prep_instance_hdr(xpath, area, "default", arguments);
- notif_prepr_iface_hdr(xpath, circuit, arguments);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath);
- data = yang_data_new(xpath_arg, raw_pdu);
- listnode_add(arguments, data);
-
- nb_notification_send(xpath, arguments);
-}
-
-/*
- * XPath:
- * /frr-isisd:authentication-failure
- */
-void isis_notif_authentication_failure(const struct isis_circuit *circuit,
- const char *raw_pdu)
-{
- const char *xpath = "/frr-isisd:authentication-failure";
- struct list *arguments = yang_data_list_new();
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
- struct isis_area *area = circuit->area;
-
- notif_prep_instance_hdr(xpath, area, "default", arguments);
- notif_prepr_iface_hdr(xpath, circuit, arguments);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath);
- data = yang_data_new(xpath_arg, raw_pdu);
- listnode_add(arguments, data);
-
- nb_notification_send(xpath, arguments);
-}
-
-/*
- * XPath:
- * /frr-isisd:adjacency-state-change
- */
-void isis_notif_adj_state_change(const struct isis_adjacency *adj,
- int new_state, const char *reason)
-{
- const char *xpath = "/frr-isisd:adjacency-state-change";
- struct list *arguments = yang_data_list_new();
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
- struct isis_circuit *circuit = adj->circuit;
- struct isis_area *area = circuit->area;
- struct isis_dynhn *dyn = dynhn_find_by_id(adj->sysid);
-
- notif_prep_instance_hdr(xpath, area, "default", arguments);
- notif_prepr_iface_hdr(xpath, circuit, arguments);
- if (dyn) {
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/neighbor", xpath);
- data = yang_data_new_string(xpath_arg, dyn->hostname);
- listnode_add(arguments, data);
- }
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/neighbor-system-id", xpath);
- data = yang_data_new_string(xpath_arg, sysid_print(adj->sysid));
- listnode_add(arguments, data);
-
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/state", xpath);
- data = yang_data_new_string(xpath_arg, isis_yang_adj_state(new_state));
- listnode_add(arguments, data);
- if (new_state == ISIS_ADJ_DOWN) {
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/reason", xpath);
- data = yang_data_new_string(xpath_arg, reason);
- listnode_add(arguments, data);
- }
-
- nb_notification_send(xpath, arguments);
-}
-
-/*
- * XPath:
- * /frr-isisd:rejected-adjacency
- */
-void isis_notif_reject_adjacency(const struct isis_circuit *circuit,
- const char *reason, const char *raw_pdu)
-{
- const char *xpath = "/frr-isisd:rejected-adjacency";
- struct list *arguments = yang_data_list_new();
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
- struct isis_area *area = circuit->area;
-
- notif_prep_instance_hdr(xpath, area, "default", arguments);
- notif_prepr_iface_hdr(xpath, circuit, arguments);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/reason", xpath);
- data = yang_data_new_string(xpath_arg, reason);
- listnode_add(arguments, data);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath);
- data = yang_data_new(xpath_arg, raw_pdu);
- listnode_add(arguments, data);
-
- nb_notification_send(xpath, arguments);
-}
-
-/*
- * XPath:
- * /frr-isisd:area-mismatch
- */
-void isis_notif_area_mismatch(const struct isis_circuit *circuit,
- const char *raw_pdu)
-{
- const char *xpath = "/frr-isisd:area-mismatch";
- struct list *arguments = yang_data_list_new();
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
- struct isis_area *area = circuit->area;
-
- notif_prep_instance_hdr(xpath, area, "default", arguments);
- notif_prepr_iface_hdr(xpath, circuit, arguments);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath);
- data = yang_data_new(xpath_arg, raw_pdu);
- listnode_add(arguments, data);
-
- nb_notification_send(xpath, arguments);
-}
-
-/*
- * XPath:
- * /frr-isisd:lsp-received
- */
-void isis_notif_lsp_received(const struct isis_circuit *circuit,
- const char *lsp_id, uint32_t seqno,
- uint32_t timestamp, const char *sys_id)
-{
- const char *xpath = "/frr-isisd:lsp-received";
- struct list *arguments = yang_data_list_new();
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
- struct isis_area *area = circuit->area;
-
- notif_prep_instance_hdr(xpath, area, "default", arguments);
- notif_prepr_iface_hdr(xpath, circuit, arguments);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
- data = yang_data_new_string(xpath_arg, lsp_id);
- listnode_add(arguments, data);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/sequence", xpath);
- data = yang_data_new_uint32(xpath_arg, seqno);
- listnode_add(arguments, data);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/received-timestamp", xpath);
- data = yang_data_new_uint32(xpath_arg, timestamp);
- listnode_add(arguments, data);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/neighbor-system-id", xpath);
- data = yang_data_new_string(xpath_arg, sys_id);
- listnode_add(arguments, data);
-
- nb_notification_send(xpath, arguments);
-}
-
-/*
- * XPath:
- * /frr-isisd:lsp-generation
- */
-void isis_notif_lsp_gen(const struct isis_area *area, const char *lsp_id,
- uint32_t seqno, uint32_t timestamp)
-{
- const char *xpath = "/frr-isisd:lsp-generation";
- struct list *arguments = yang_data_list_new();
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
-
- notif_prep_instance_hdr(xpath, area, "default", arguments);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
- data = yang_data_new_string(xpath_arg, lsp_id);
- listnode_add(arguments, data);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/sequence", xpath);
- data = yang_data_new_uint32(xpath_arg, seqno);
- listnode_add(arguments, data);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/send-timestamp", xpath);
- data = yang_data_new_uint32(xpath_arg, timestamp);
- listnode_add(arguments, data);
-
- nb_notification_send(xpath, arguments);
-}
-
-/*
- * XPath:
- * /frr-isisd:id-len-mismatch
- */
-void isis_notif_id_len_mismatch(const struct isis_circuit *circuit,
- uint8_t rcv_id_len, const char *raw_pdu)
-{
- const char *xpath = "/frr-isisd:id-len-mismatch";
- struct list *arguments = yang_data_list_new();
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
- struct isis_area *area = circuit->area;
-
- notif_prep_instance_hdr(xpath, area, "default", arguments);
- notif_prepr_iface_hdr(xpath, circuit, arguments);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/pdu-field-len", xpath);
- data = yang_data_new_uint8(xpath_arg, rcv_id_len);
- listnode_add(arguments, data);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath);
- data = yang_data_new(xpath_arg, raw_pdu);
- listnode_add(arguments, data);
-
- nb_notification_send(xpath, arguments);
-}
-
-/*
- * XPath:
- * /frr-isisd:version-skew
- */
-void isis_notif_version_skew(const struct isis_circuit *circuit,
- uint8_t version, const char *raw_pdu)
-{
- const char *xpath = "/frr-isisd:version-skew";
- struct list *arguments = yang_data_list_new();
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
- struct isis_area *area = circuit->area;
-
- notif_prep_instance_hdr(xpath, area, "default", arguments);
- notif_prepr_iface_hdr(xpath, circuit, arguments);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/protocol-version", xpath);
- data = yang_data_new_uint8(xpath_arg, version);
- listnode_add(arguments, data);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath);
- data = yang_data_new(xpath_arg, raw_pdu);
- listnode_add(arguments, data);
-
- nb_notification_send(xpath, arguments);
-}
-
-/*
- * XPath:
- * /frr-isisd:lsp-error-detected
- */
-void isis_notif_lsp_error(const struct isis_circuit *circuit,
- const char *lsp_id, const char *raw_pdu,
- __attribute__((unused)) uint32_t offset,
- __attribute__((unused)) uint8_t tlv_type)
-{
- const char *xpath = "/frr-isisd:lsp-error-detected";
- struct list *arguments = yang_data_list_new();
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
- struct isis_area *area = circuit->area;
-
- notif_prep_instance_hdr(xpath, area, "default", arguments);
- notif_prepr_iface_hdr(xpath, circuit, arguments);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
- data = yang_data_new_string(xpath_arg, lsp_id);
- listnode_add(arguments, data);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath);
- data = yang_data_new(xpath_arg, raw_pdu);
- listnode_add(arguments, data);
- /* ignore offset and tlv_type which cannot be set properly */
-
- nb_notification_send(xpath, arguments);
-}
-
-/*
- * XPath:
- * /frr-isisd:sequence-number-skipped
- */
-void isis_notif_seqno_skipped(const struct isis_circuit *circuit,
- const char *lsp_id)
-{
- const char *xpath = "/frr-isisd:sequence-number-skipped";
- struct list *arguments = yang_data_list_new();
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
- struct isis_area *area = circuit->area;
-
- notif_prep_instance_hdr(xpath, area, "default", arguments);
- notif_prepr_iface_hdr(xpath, circuit, arguments);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
- data = yang_data_new_string(xpath_arg, lsp_id);
- listnode_add(arguments, data);
-
- nb_notification_send(xpath, arguments);
-}
-
-/*
- * XPath:
- * /frr-isisd:own-lsp-purge
- */
-void isis_notif_own_lsp_purge(const struct isis_circuit *circuit,
- const char *lsp_id)
-{
- const char *xpath = "/frr-isisd:own-lsp-purge";
- struct list *arguments = yang_data_list_new();
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
- struct isis_area *area = circuit->area;
-
- notif_prep_instance_hdr(xpath, area, "default", arguments);
- notif_prepr_iface_hdr(xpath, circuit, arguments);
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
- data = yang_data_new_string(xpath_arg, lsp_id);
- listnode_add(arguments, data);
-
- nb_notification_send(xpath, arguments);
-}
-
-/* clang-format off */
-const struct frr_yang_module_info frr_isisd_info = {
- .name = "frr-isisd",
- .nodes = {
- {
- .xpath = "/frr-isisd:isis/instance",
- .cbs = {
- .cli_show = cli_show_router_isis,
- .create = isis_instance_create,
- .destroy = isis_instance_destroy,
- },
- .priority = NB_DFLT_PRIORITY - 1,
- },
- {
- .xpath = "/frr-isisd:isis/instance/is-type",
- .cbs = {
- .cli_show = cli_show_isis_is_type,
- .modify = isis_instance_is_type_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/area-address",
- .cbs = {
- .cli_show = cli_show_isis_area_address,
- .create = isis_instance_area_address_create,
- .destroy = isis_instance_area_address_destroy,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/dynamic-hostname",
- .cbs = {
- .cli_show = cli_show_isis_dynamic_hostname,
- .modify = isis_instance_dynamic_hostname_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/attached",
- .cbs = {
- .cli_show = cli_show_isis_attached,
- .modify = isis_instance_attached_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/overload",
- .cbs = {
- .cli_show = cli_show_isis_overload,
- .modify = isis_instance_overload_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/metric-style",
- .cbs = {
- .cli_show = cli_show_isis_metric_style,
- .modify = isis_instance_metric_style_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/purge-originator",
- .cbs = {
- .cli_show = cli_show_isis_purge_origin,
- .modify = isis_instance_purge_originator_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/lsp/mtu",
- .cbs = {
- .cli_show = cli_show_isis_lsp_mtu,
- .modify = isis_instance_lsp_mtu_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/lsp/refresh-interval",
- .cbs = {
- .cli_show = cli_show_isis_lsp_ref_interval,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/lsp/refresh-interval/level-1",
- .cbs = {
- .modify = isis_instance_lsp_refresh_interval_level_1_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/lsp/refresh-interval/level-2",
- .cbs = {
- .modify = isis_instance_lsp_refresh_interval_level_2_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/lsp/maximum-lifetime",
- .cbs = {
- .cli_show = cli_show_isis_lsp_max_lifetime,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/lsp/maximum-lifetime/level-1",
- .cbs = {
- .modify = isis_instance_lsp_maximum_lifetime_level_1_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/lsp/maximum-lifetime/level-2",
- .cbs = {
- .modify = isis_instance_lsp_maximum_lifetime_level_2_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/lsp/generation-interval",
- .cbs = {
- .cli_show = cli_show_isis_lsp_gen_interval,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/lsp/generation-interval/level-1",
- .cbs = {
- .modify = isis_instance_lsp_generation_interval_level_1_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/lsp/generation-interval/level-2",
- .cbs = {
- .modify = isis_instance_lsp_generation_interval_level_2_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay",
- .cbs = {
- .apply_finish = ietf_backoff_delay_apply_finish,
- .cli_show = cli_show_isis_spf_ietf_backoff,
- .create = isis_instance_spf_ietf_backoff_delay_create,
- .destroy = isis_instance_spf_ietf_backoff_delay_destroy,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay/init-delay",
- .cbs = {
- .modify = isis_instance_spf_ietf_backoff_delay_init_delay_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay/short-delay",
- .cbs = {
- .modify = isis_instance_spf_ietf_backoff_delay_short_delay_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay/long-delay",
- .cbs = {
- .modify = isis_instance_spf_ietf_backoff_delay_long_delay_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay/hold-down",
- .cbs = {
- .modify = isis_instance_spf_ietf_backoff_delay_hold_down_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay/time-to-learn",
- .cbs = {
- .modify = isis_instance_spf_ietf_backoff_delay_time_to_learn_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/spf/minimum-interval",
- .cbs = {
- .cli_show = cli_show_isis_spf_min_interval,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/spf/minimum-interval/level-1",
- .cbs = {
- .modify = isis_instance_spf_minimum_interval_level_1_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/spf/minimum-interval/level-2",
- .cbs = {
- .modify = isis_instance_spf_minimum_interval_level_2_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/area-password",
- .cbs = {
- .apply_finish = area_password_apply_finish,
- .cli_show = cli_show_isis_area_pwd,
- .create = isis_instance_area_password_create,
- .destroy = isis_instance_area_password_destroy,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/area-password/password",
- .cbs = {
- .modify = isis_instance_area_password_password_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/area-password/password-type",
- .cbs = {
- .modify = isis_instance_area_password_password_type_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/area-password/authenticate-snp",
- .cbs = {
- .modify = isis_instance_area_password_authenticate_snp_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/domain-password",
- .cbs = {
- .apply_finish = domain_password_apply_finish,
- .cli_show = cli_show_isis_domain_pwd,
- .create = isis_instance_domain_password_create,
- .destroy = isis_instance_domain_password_destroy,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/domain-password/password",
- .cbs = {
- .modify = isis_instance_domain_password_password_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/domain-password/password-type",
- .cbs = {
- .modify = isis_instance_domain_password_password_type_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/domain-password/authenticate-snp",
- .cbs = {
- .modify = isis_instance_domain_password_authenticate_snp_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv4",
- .cbs = {
- .apply_finish = default_info_origin_ipv4_apply_finish,
- .cli_show = cli_show_isis_def_origin_ipv4,
- .create = isis_instance_default_information_originate_ipv4_create,
- .destroy = isis_instance_default_information_originate_ipv4_destroy,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv4/always",
- .cbs = {
- .modify = isis_instance_default_information_originate_ipv4_always_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv4/route-map",
- .cbs = {
- .destroy = isis_instance_default_information_originate_ipv4_route_map_destroy,
- .modify = isis_instance_default_information_originate_ipv4_route_map_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv4/metric",
- .cbs = {
- .modify = isis_instance_default_information_originate_ipv4_metric_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv6",
- .cbs = {
- .apply_finish = default_info_origin_ipv6_apply_finish,
- .cli_show = cli_show_isis_def_origin_ipv6,
- .create = isis_instance_default_information_originate_ipv6_create,
- .destroy = isis_instance_default_information_originate_ipv6_destroy,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv6/always",
- .cbs = {
- .modify = isis_instance_default_information_originate_ipv6_always_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv6/route-map",
- .cbs = {
- .destroy = isis_instance_default_information_originate_ipv6_route_map_destroy,
- .modify = isis_instance_default_information_originate_ipv6_route_map_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv6/metric",
- .cbs = {
- .modify = isis_instance_default_information_originate_ipv6_metric_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/redistribute/ipv4",
- .cbs = {
- .apply_finish = redistribute_ipv4_apply_finish,
- .cli_show = cli_show_isis_redistribute_ipv4,
- .create = isis_instance_redistribute_ipv4_create,
- .destroy = isis_instance_redistribute_ipv4_destroy,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/redistribute/ipv4/route-map",
- .cbs = {
- .destroy = isis_instance_redistribute_ipv4_route_map_destroy,
- .modify = isis_instance_redistribute_ipv4_route_map_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/redistribute/ipv4/metric",
- .cbs = {
- .modify = isis_instance_redistribute_ipv4_metric_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/redistribute/ipv6",
- .cbs = {
- .apply_finish = redistribute_ipv6_apply_finish,
- .cli_show = cli_show_isis_redistribute_ipv6,
- .create = isis_instance_redistribute_ipv6_create,
- .destroy = isis_instance_redistribute_ipv6_destroy,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/redistribute/ipv6/route-map",
- .cbs = {
- .destroy = isis_instance_redistribute_ipv6_route_map_destroy,
- .modify = isis_instance_redistribute_ipv6_route_map_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/redistribute/ipv6/metric",
- .cbs = {
- .modify = isis_instance_redistribute_ipv6_metric_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/multi-topology/ipv4-multicast",
- .cbs = {
- .cli_show = cli_show_isis_mt_ipv4_multicast,
- .create = isis_instance_multi_topology_ipv4_multicast_create,
- .destroy = isis_instance_multi_topology_ipv4_multicast_destroy,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/multi-topology/ipv4-multicast/overload",
- .cbs = {
- .modify = isis_instance_multi_topology_ipv4_multicast_overload_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/multi-topology/ipv4-management",
- .cbs = {
- .cli_show = cli_show_isis_mt_ipv4_mgmt,
- .create = isis_instance_multi_topology_ipv4_management_create,
- .destroy = isis_instance_multi_topology_ipv4_management_destroy,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/multi-topology/ipv4-management/overload",
- .cbs = {
- .modify = isis_instance_multi_topology_ipv4_management_overload_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-unicast",
- .cbs = {
- .cli_show = cli_show_isis_mt_ipv6_unicast,
- .create = isis_instance_multi_topology_ipv6_unicast_create,
- .destroy = isis_instance_multi_topology_ipv6_unicast_destroy,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-unicast/overload",
- .cbs = {
- .modify = isis_instance_multi_topology_ipv6_unicast_overload_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-multicast",
- .cbs = {
- .cli_show = cli_show_isis_mt_ipv6_multicast,
- .create = isis_instance_multi_topology_ipv6_multicast_create,
- .destroy = isis_instance_multi_topology_ipv6_multicast_destroy,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-multicast/overload",
- .cbs = {
- .modify = isis_instance_multi_topology_ipv6_multicast_overload_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-management",
- .cbs = {
- .cli_show = cli_show_isis_mt_ipv6_mgmt,
- .create = isis_instance_multi_topology_ipv6_management_create,
- .destroy = isis_instance_multi_topology_ipv6_management_destroy,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-management/overload",
- .cbs = {
- .modify = isis_instance_multi_topology_ipv6_management_overload_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-dstsrc",
- .cbs = {
- .cli_show = cli_show_isis_mt_ipv6_dstsrc,
- .create = isis_instance_multi_topology_ipv6_dstsrc_create,
- .destroy = isis_instance_multi_topology_ipv6_dstsrc_destroy,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-dstsrc/overload",
- .cbs = {
- .modify = isis_instance_multi_topology_ipv6_dstsrc_overload_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/log-adjacency-changes",
- .cbs = {
- .cli_show = cli_show_isis_log_adjacency,
- .modify = isis_instance_log_adjacency_changes_modify,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/mpls-te",
- .cbs = {
- .cli_show = cli_show_isis_mpls_te,
- .create = isis_instance_mpls_te_create,
- .destroy = isis_instance_mpls_te_destroy,
- },
- },
- {
- .xpath = "/frr-isisd:isis/instance/mpls-te/router-address",
- .cbs = {
- .cli_show = cli_show_isis_mpls_te_router_addr,
- .destroy = isis_instance_mpls_te_router_address_destroy,
- .modify = isis_instance_mpls_te_router_address_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis",
- .cbs = {
- .create = lib_interface_isis_create,
- .destroy = lib_interface_isis_destroy,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/area-tag",
- .cbs = {
- .modify = lib_interface_isis_area_tag_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/circuit-type",
- .cbs = {
- .cli_show = cli_show_ip_isis_circ_type,
- .modify = lib_interface_isis_circuit_type_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/ipv4-routing",
- .cbs = {
- .cli_show = cli_show_ip_isis_ipv4,
- .modify = lib_interface_isis_ipv4_routing_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/ipv6-routing",
- .cbs = {
- .cli_show = cli_show_ip_isis_ipv6,
- .modify = lib_interface_isis_ipv6_routing_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring",
- .cbs = {
- .modify = lib_interface_isis_bfd_monitoring_modify,
- .cli_show = cli_show_ip_isis_bfd_monitoring,
- }
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/csnp-interval",
- .cbs = {
- .cli_show = cli_show_ip_isis_csnp_interval,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/csnp-interval/level-1",
- .cbs = {
- .modify = lib_interface_isis_csnp_interval_level_1_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/csnp-interval/level-2",
- .cbs = {
- .modify = lib_interface_isis_csnp_interval_level_2_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/psnp-interval",
- .cbs = {
- .cli_show = cli_show_ip_isis_psnp_interval,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/psnp-interval/level-1",
- .cbs = {
- .modify = lib_interface_isis_psnp_interval_level_1_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/psnp-interval/level-2",
- .cbs = {
- .modify = lib_interface_isis_psnp_interval_level_2_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/padding",
- .cbs = {
- .cli_show = cli_show_ip_isis_hello_padding,
- .modify = lib_interface_isis_hello_padding_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/interval",
- .cbs = {
- .cli_show = cli_show_ip_isis_hello_interval,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-1",
- .cbs = {
- .modify = lib_interface_isis_hello_interval_level_1_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-2",
- .cbs = {
- .modify = lib_interface_isis_hello_interval_level_2_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/multiplier",
- .cbs = {
- .cli_show = cli_show_ip_isis_hello_multi,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/multiplier/level-1",
- .cbs = {
- .modify = lib_interface_isis_hello_multiplier_level_1_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/multiplier/level-2",
- .cbs = {
- .modify = lib_interface_isis_hello_multiplier_level_2_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/metric",
- .cbs = {
- .cli_show = cli_show_ip_isis_metric,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/metric/level-1",
- .cbs = {
- .modify = lib_interface_isis_metric_level_1_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/metric/level-2",
- .cbs = {
- .modify = lib_interface_isis_metric_level_2_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/priority",
- .cbs = {
- .cli_show = cli_show_ip_isis_priority,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/priority/level-1",
- .cbs = {
- .modify = lib_interface_isis_priority_level_1_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/priority/level-2",
- .cbs = {
- .modify = lib_interface_isis_priority_level_2_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/network-type",
- .cbs = {
- .cli_show = cli_show_ip_isis_network_type,
- .modify = lib_interface_isis_network_type_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/passive",
- .cbs = {
- .cli_show = cli_show_ip_isis_passive,
- .modify = lib_interface_isis_passive_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/password",
- .cbs = {
- .cli_show = cli_show_ip_isis_password,
- .create = lib_interface_isis_password_create,
- .destroy = lib_interface_isis_password_destroy,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/password/password",
- .cbs = {
- .modify = lib_interface_isis_password_password_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/password/password-type",
- .cbs = {
- .modify = lib_interface_isis_password_password_type_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/disable-three-way-handshake",
- .cbs = {
- .cli_show = cli_show_ip_isis_threeway_shake,
- .modify = lib_interface_isis_disable_three_way_handshake_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-unicast",
- .cbs = {
- .cli_show = cli_show_ip_isis_mt_ipv4_unicast,
- .modify = lib_interface_isis_multi_topology_ipv4_unicast_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-multicast",
- .cbs = {
- .cli_show = cli_show_ip_isis_mt_ipv4_multicast,
- .modify = lib_interface_isis_multi_topology_ipv4_multicast_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-management",
- .cbs = {
- .cli_show = cli_show_ip_isis_mt_ipv4_mgmt,
- .modify = lib_interface_isis_multi_topology_ipv4_management_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-unicast",
- .cbs = {
- .cli_show = cli_show_ip_isis_mt_ipv6_unicast,
- .modify = lib_interface_isis_multi_topology_ipv6_unicast_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-multicast",
- .cbs = {
- .cli_show = cli_show_ip_isis_mt_ipv6_multicast,
- .modify = lib_interface_isis_multi_topology_ipv6_multicast_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-management",
- .cbs = {
- .cli_show = cli_show_ip_isis_mt_ipv6_mgmt,
- .modify = lib_interface_isis_multi_topology_ipv6_management_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-dstsrc",
- .cbs = {
- .cli_show = cli_show_ip_isis_mt_ipv6_dstsrc,
- .modify = lib_interface_isis_multi_topology_ipv6_dstsrc_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency",
- .cbs = {
- .get_next = lib_interface_isis_adjacencies_adjacency_get_next,
- }
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/neighbor-sys-type",
- .cbs = {
- .get_elem = lib_interface_isis_adjacencies_adjacency_neighbor_sys_type_get_elem,
- }
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/neighbor-sysid",
- .cbs = {
- .get_elem = lib_interface_isis_adjacencies_adjacency_neighbor_sysid_get_elem,
- }
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/neighbor-extended-circuit-id",
- .cbs = {
- .get_elem = lib_interface_isis_adjacencies_adjacency_neighbor_extended_circuit_id_get_elem,
- }
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/neighbor-snpa",
- .cbs = {
- .get_elem = lib_interface_isis_adjacencies_adjacency_neighbor_snpa_get_elem,
- }
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/hold-timer",
- .cbs = {
- .get_elem = lib_interface_isis_adjacencies_adjacency_hold_timer_get_elem,
- }
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/neighbor-priority",
- .cbs = {
- .get_elem = lib_interface_isis_adjacencies_adjacency_neighbor_priority_get_elem,
- }
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/adjacencies/adjacency/state",
- .cbs = {
- .get_elem = lib_interface_isis_adjacencies_adjacency_state_get_elem,
- }
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/event-counters/adjacency-changes",
- .cbs = {
- .get_elem = lib_interface_isis_event_counters_adjacency_changes_get_elem,
- }
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/event-counters/adjacency-number",
- .cbs = {
- .get_elem = lib_interface_isis_event_counters_adjacency_number_get_elem,
- }
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/event-counters/init-fails",
- .cbs = {
- .get_elem = lib_interface_isis_event_counters_init_fails_get_elem,
- }
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/event-counters/adjacency-rejects",
- .cbs = {
- .get_elem = lib_interface_isis_event_counters_adjacency_rejects_get_elem,
- }
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/event-counters/id-len-mismatch",
- .cbs = {
- .get_elem = lib_interface_isis_event_counters_id_len_mismatch_get_elem,
- }
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/event-counters/max-area-addresses-mismatch",
- .cbs = {
- .get_elem = lib_interface_isis_event_counters_max_area_addresses_mismatch_get_elem,
- }
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/event-counters/authentication-type-fails",
- .cbs = {
- .get_elem = lib_interface_isis_event_counters_authentication_type_fails_get_elem,
- }
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/event-counters/authentication-fails",
- .cbs = {
- .get_elem = lib_interface_isis_event_counters_authentication_fails_get_elem,
- }
- },
- {
- .xpath = NULL,
- },
- }
-};
diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c
index a637ff003f..71249cf658 100644
--- a/isisd/isis_pdu.c
+++ b/isisd/isis_pdu.c
@@ -58,6 +58,7 @@
#include "isisd/fabricd.h"
#include "isisd/isis_tx_queue.h"
#include "isisd/isis_pdu_counter.h"
+#include "isisd/isis_nb.h"
static int ack_lsp(struct isis_lsp_hdr *hdr, struct isis_circuit *circuit,
int level)
diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c
index bdf6869f5c..b4c699ccbb 100644
--- a/isisd/isis_zebra.c
+++ b/isisd/isis_zebra.c
@@ -128,7 +128,7 @@ static int isis_zebra_if_address_del(ZAPI_CALLBACK_ARGS)
if (if_is_operative(ifp))
isis_circuit_del_addr(circuit_scan_by_ifp(ifp), c);
- connected_free(c);
+ connected_free(&c);
return 0;
}
diff --git a/isisd/isisd.c b/isisd/isisd.c
index 029a9e0688..f15d7a9c7e 100644
--- a/isisd/isisd.c
+++ b/isisd/isisd.c
@@ -57,6 +57,7 @@
#include "isisd/isis_te.h"
#include "isisd/isis_mt.h"
#include "isisd/fabricd.h"
+#include "isisd/isis_nb.h"
struct isis *isis = NULL;
diff --git a/isisd/isisd.h b/isisd/isisd.h
index f825b6ecb4..052cc10928 100644
--- a/isisd/isisd.h
+++ b/isisd/isisd.h
@@ -219,52 +219,6 @@ int isis_area_passwd_cleartext_set(struct isis_area *area, int level,
int isis_area_passwd_hmac_md5_set(struct isis_area *area, int level,
const char *passwd, uint8_t snp_auth);
-extern const struct frr_yang_module_info frr_isisd_info;
-extern void isis_northbound_init(void);
-
-/* YANG northbound notifications */
-extern void isis_notif_db_overload(const struct isis_area *area, bool overload);
-extern void isis_notif_lsp_too_large(const struct isis_circuit *circuit,
- uint32_t pdu_size, const char *lsp_id);
-extern void isis_notif_if_state_change(const struct isis_circuit *circuit,
- bool down);
-extern void isis_notif_corrupted_lsp(const struct isis_area *area,
- const char *lsp_id); /* currently unused */
-extern void isis_notif_lsp_exceed_max(const struct isis_area *area,
- const char *lsp_id);
-extern void
-isis_notif_max_area_addr_mismatch(const struct isis_circuit *circuit,
- uint8_t max_area_addrs, const char *raw_pdu);
-extern void
-isis_notif_authentication_type_failure(const struct isis_circuit *circuit,
- const char *raw_pdu);
-extern void
-isis_notif_authentication_failure(const struct isis_circuit *circuit,
- const char *raw_pdu);
-extern void isis_notif_adj_state_change(const struct isis_adjacency *adj,
- int new_state, const char *reason);
-extern void isis_notif_reject_adjacency(const struct isis_circuit *circuit,
- const char *reason,
- const char *raw_pdu);
-extern void isis_notif_area_mismatch(const struct isis_circuit *circuit,
- const char *raw_pdu);
-extern void isis_notif_lsp_received(const struct isis_circuit *circuit,
- const char *lsp_id, uint32_t seqno,
- uint32_t timestamp, const char *sys_id);
-extern void isis_notif_lsp_gen(const struct isis_area *area, const char *lsp_id,
- uint32_t seqno, uint32_t timestamp);
-extern void isis_notif_id_len_mismatch(const struct isis_circuit *circuit,
- uint8_t rcv_id_len, const char *raw_pdu);
-extern void isis_notif_version_skew(const struct isis_circuit *circuit,
- uint8_t version, const char *raw_pdu);
-extern void isis_notif_lsp_error(const struct isis_circuit *circuit,
- const char *lsp_id, const char *raw_pdu,
- uint32_t offset, uint8_t tlv_type);
-extern void isis_notif_seqno_skipped(const struct isis_circuit *circuit,
- const char *lsp_id);
-extern void isis_notif_own_lsp_purge(const struct isis_circuit *circuit,
- const char *lsp_id);
-
/* Master of threads. */
extern struct thread_master *master;
diff --git a/isisd/subdir.am b/isisd/subdir.am
index 24f24022a5..e77fef41dd 100644
--- a/isisd/subdir.am
+++ b/isisd/subdir.am
@@ -39,6 +39,7 @@ noinst_HEADERS += \
isisd/isis_memory.h \
isisd/isis_misc.h \
isisd/isis_mt.h \
+ isisd/isis_nb.h \
isisd/isis_network.h \
isisd/isis_pdu.h \
isisd/isis_pdu_counter.h \
@@ -54,7 +55,6 @@ noinst_HEADERS += \
isisd/isisd.h \
isisd/iso_checksum.h \
isisd/fabricd.h \
- isisd/isis_cli.h \
# end
LIBISIS_SOURCES = \
@@ -99,7 +99,10 @@ ISIS_LDADD_COMMON = lib/libfrr.la $(LIBCAP)
isisd_libisis_a_SOURCES = \
$(LIBISIS_SOURCES) \
- isisd/isis_northbound.c \
+ isisd/isis_nb.c \
+ isisd/isis_nb_config.c \
+ isisd/isis_nb_notifications.c \
+ isisd/isis_nb_state.c \
isisd/isis_cli.c \
#end
diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c
index 4df1fc0304..946b51e4ee 100644
--- a/ldpd/ldp_zebra.c
+++ b/ldpd/ldp_zebra.c
@@ -368,7 +368,7 @@ ldp_interface_address_delete(ZAPI_CALLBACK_ARGS)
ifp = ifc->ifp;
ifc2kaddr(ifp, ifc, &ka);
- connected_free(ifc);
+ connected_free(&ifc);
/* Filter invalid addresses. */
if (bad_addr(ka.af, &ka.addr))
diff --git a/lib/bfd.c b/lib/bfd.c
index 00dbd1b3d1..ffb3cbc1f8 100644
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -315,6 +315,8 @@ const char *bfd_get_status_str(int status)
return "Down";
case BFD_STATUS_UP:
return "Up";
+ case BFD_STATUS_ADMIN_DOWN:
+ return "Admin Down";
case BFD_STATUS_UNKNOWN:
default:
return "Unknown";
diff --git a/lib/bfd.h b/lib/bfd.h
index e4781f4eaf..7f5d111504 100644
--- a/lib/bfd.h
+++ b/lib/bfd.h
@@ -51,9 +51,17 @@ struct bfd_gbl {
#define BFD_FLAG_BFD_CBIT_ON (1 << 3) /* Peer registered with CBIT set to on */
#define BFD_FLAG_BFD_CHECK_CONTROLPLANE (1 << 4) /* BFD and controlplane daemon are linked */
-#define BFD_STATUS_UNKNOWN (1 << 0) /* BFD session status never received */
-#define BFD_STATUS_DOWN (1 << 1) /* BFD session status is down */
-#define BFD_STATUS_UP (1 << 2) /* BFD session status is up */
+#define BFD_STATUS_UNKNOWN (1 << 0) /* BFD session status never received */
+#define BFD_STATUS_DOWN (1 << 1) /* BFD session status is down */
+#define BFD_STATUS_UP (1 << 2) /* BFD session status is up */
+#define BFD_STATUS_ADMIN_DOWN (1 << 3) /* BFD session is admin down */
+
+#define BFD_SET_CLIENT_STATUS(current_status, new_status) \
+ do { \
+ (current_status) = \
+ (((new_status) == BFD_STATUS_ADMIN_DOWN) ? \
+ BFD_STATUS_DOWN : (new_status));\
+ } while (0)
enum bfd_sess_type {
BFD_TYPE_NOT_CONFIGURED,
diff --git a/lib/if.c b/lib/if.c
index c704ea9bc9..9d0f13ecbd 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -140,6 +140,13 @@ static int if_cmp_index_func(const struct interface *ifp1,
return ifp1->ifindex - ifp2->ifindex;
}
+static void ifp_connected_free(void *arg)
+{
+ struct connected *c = arg;
+
+ connected_free(&c);
+}
+
/* Create new interface structure. */
static struct interface *if_new(vrf_id_t vrf_id)
{
@@ -153,7 +160,7 @@ static struct interface *if_new(vrf_id_t vrf_id)
ifp->vrf_id = vrf_id;
ifp->connected = list_new();
- ifp->connected->del = (void (*)(void *))connected_free;
+ ifp->connected->del = ifp_connected_free;
ifp->nbr_connected = list_new();
ifp->nbr_connected->del = (void (*)(void *))nbr_connected_free;
@@ -178,7 +185,7 @@ void if_destroy_via_zapi(struct interface *ifp)
if_set_index(ifp, IFINDEX_INTERNAL);
if (!ifp->configured)
- if_delete(ifp);
+ if_delete(&ifp);
}
void if_up_via_zapi(struct interface *ifp)
@@ -276,27 +283,29 @@ void if_delete_retain(struct interface *ifp)
}
/* Delete and free interface structure. */
-void if_delete(struct interface *ifp)
+void if_delete(struct interface **ifp)
{
+ struct interface *ptr = *ifp;
struct vrf *vrf;
- vrf = vrf_lookup_by_id(ifp->vrf_id);
+ vrf = vrf_lookup_by_id(ptr->vrf_id);
assert(vrf);
- IFNAME_RB_REMOVE(vrf, ifp);
- if (ifp->ifindex != IFINDEX_INTERNAL)
- IFINDEX_RB_REMOVE(vrf, ifp);
+ IFNAME_RB_REMOVE(vrf, ptr);
+ if (ptr->ifindex != IFINDEX_INTERNAL)
+ IFINDEX_RB_REMOVE(vrf, ptr);
- if_delete_retain(ifp);
+ if_delete_retain(ptr);
- list_delete(&ifp->connected);
- list_delete(&ifp->nbr_connected);
+ list_delete(&ptr->connected);
+ list_delete(&ptr->nbr_connected);
- if_link_params_free(ifp);
+ if_link_params_free(ptr);
- XFREE(MTYPE_TMP, ifp->desc);
+ XFREE(MTYPE_TMP, ptr->desc);
- XFREE(MTYPE_IF, ifp);
+ XFREE(MTYPE_IF, ptr);
+ *ifp = NULL;
}
/* Used only internally to check within VRF only */
@@ -866,24 +875,27 @@ struct nbr_connected *nbr_connected_new(void)
}
/* Free connected structure. */
-void connected_free(struct connected *connected)
+void connected_free(struct connected **connected)
{
- if (connected->address)
- prefix_free(connected->address);
+ struct connected *ptr = *connected;
- if (connected->destination)
- prefix_free(connected->destination);
+ if (ptr->address)
+ prefix_free(&ptr->address);
- XFREE(MTYPE_CONNECTED_LABEL, connected->label);
+ if (ptr->destination)
+ prefix_free(&ptr->destination);
- XFREE(MTYPE_CONNECTED, connected);
+ XFREE(MTYPE_CONNECTED_LABEL, ptr->label);
+
+ XFREE(MTYPE_CONNECTED, ptr);
+ *connected = NULL;
}
/* Free nbr connected structure. */
void nbr_connected_free(struct nbr_connected *connected)
{
if (connected->address)
- prefix_free(connected->address);
+ prefix_free(&connected->address);
XFREE(MTYPE_NBR_CONNECTED, connected);
}
@@ -959,6 +971,20 @@ static int connected_same_prefix(struct prefix *p1, struct prefix *p2)
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)
+{
+ struct listnode *cnode;
+ struct connected *connected;
+ unsigned int cnt = 0;
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected))
+ if (connected->address->family == family)
+ cnt++;
+
+ return cnt;
+}
+
struct connected *connected_lookup_prefix_exact(struct interface *ifp,
struct prefix *p)
{
@@ -1145,7 +1171,7 @@ void if_terminate(struct vrf *vrf)
ifp->node->info = NULL;
route_unlock_node(ifp->node);
}
- if_delete(ifp);
+ if_delete(&ifp);
}
}
@@ -1527,7 +1553,7 @@ static int lib_interface_destroy(enum nb_event event,
ifp = nb_running_unset_entry(dnode);
ifp->configured = false;
- if_delete(ifp);
+ if_delete(&ifp);
break;
}
@@ -1585,7 +1611,7 @@ static const void *lib_interface_lookup_entry(const void *parent_list_entry,
const char *vrfname = keys->key[1];
struct vrf *vrf = vrf_lookup_by_name(vrfname);
- return if_lookup_by_name(ifname, vrf->vrf_id);
+ return vrf ? if_lookup_by_name(ifname, vrf->vrf_id) : NULL;
}
/*
diff --git a/lib/if.h b/lib/if.h
index 1ee6561e8d..4ca2e79572 100644
--- a/lib/if.h
+++ b/lib/if.h
@@ -513,7 +513,7 @@ extern void if_delete_retain(struct interface *);
/* Delete and free the interface structure: calls if_delete_retain and then
deletes it from the interface list and frees the structure. */
-extern void if_delete(struct interface *);
+extern void if_delete(struct interface **ifp);
extern int if_is_up(const struct interface *ifp);
extern int if_is_running(const struct interface *ifp);
@@ -543,7 +543,7 @@ extern ifindex_t ifname2ifindex(const char *ifname, vrf_id_t vrf_id);
/* Connected address functions. */
extern struct connected *connected_new(void);
-extern void connected_free(struct connected *);
+extern void connected_free(struct connected **connected);
extern void connected_add(struct interface *, struct connected *);
extern struct connected *
connected_add_by_prefix(struct interface *, struct prefix *, struct prefix *);
@@ -553,6 +553,7 @@ extern struct connected *connected_lookup_prefix(struct interface *,
struct prefix *);
extern struct connected *connected_lookup_prefix_exact(struct interface *,
struct prefix *);
+extern unsigned int connected_count_by_family(struct interface *, int family);
extern struct nbr_connected *nbr_connected_new(void);
extern void nbr_connected_free(struct nbr_connected *);
struct nbr_connected *nbr_connected_check(struct interface *, struct prefix *);
diff --git a/lib/nexthop.c b/lib/nexthop.c
index cf5bed3d62..73c2de0cd8 100644
--- a/lib/nexthop.c
+++ b/lib/nexthop.c
@@ -349,7 +349,7 @@ const char *nexthop2str(const struct nexthop *nexthop, char *str, int size)
* left branch is 'resolved' and right branch is 'next':
* https://en.wikipedia.org/wiki/Tree_traversal#/media/File:Sorted_binary_tree_preorder.svg
*/
-struct nexthop *nexthop_next(struct nexthop *nexthop)
+struct nexthop *nexthop_next(const struct nexthop *nexthop)
{
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
return nexthop->resolved;
@@ -364,6 +364,19 @@ struct nexthop *nexthop_next(struct nexthop *nexthop)
return NULL;
}
+/* Return the next nexthop in the tree that is resolved and active */
+struct nexthop *nexthop_next_active_resolved(const struct nexthop *nexthop)
+{
+ struct nexthop *next = nexthop_next(nexthop);
+
+ while (next
+ && (CHECK_FLAG(next->flags, NEXTHOP_FLAG_RECURSIVE)
+ || !CHECK_FLAG(next->flags, NEXTHOP_FLAG_ACTIVE)))
+ next = nexthop_next(next);
+
+ return next;
+}
+
unsigned int nexthop_level(struct nexthop *nexthop)
{
unsigned int rv = 0;
@@ -374,16 +387,13 @@ unsigned int nexthop_level(struct nexthop *nexthop)
return rv;
}
-uint32_t nexthop_hash(const struct nexthop *nexthop)
+/* Only hash word-sized things, let cmp do the rest. */
+uint32_t nexthop_hash_quick(const struct nexthop *nexthop)
{
uint32_t key = 0x45afe398;
key = jhash_3words(nexthop->type, nexthop->vrf_id,
nexthop->nh_label_type, key);
- /* gate and blackhole are together in a union */
- key = jhash(&nexthop->gate, sizeof(nexthop->gate), key);
- key = jhash(&nexthop->src, sizeof(nexthop->src), key);
- key = jhash(&nexthop->rmap_src, sizeof(nexthop->rmap_src), key);
if (nexthop->nh_label) {
int labels = nexthop->nh_label->num_labels;
@@ -410,17 +420,35 @@ uint32_t nexthop_hash(const struct nexthop *nexthop)
key = jhash_1word(nexthop->nh_label->label[i], key);
}
- switch (nexthop->type) {
- case NEXTHOP_TYPE_IPV4_IFINDEX:
- case NEXTHOP_TYPE_IPV6_IFINDEX:
- case NEXTHOP_TYPE_IFINDEX:
- key = jhash_1word(nexthop->ifindex, key);
- break;
- case NEXTHOP_TYPE_BLACKHOLE:
- case NEXTHOP_TYPE_IPV4:
- case NEXTHOP_TYPE_IPV6:
- break;
- }
+ key = jhash_2words(nexthop->ifindex,
+ CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK),
+ key);
+
+ return key;
+}
+
+
+#define GATE_SIZE 4 /* Number of uint32_t words in struct g_addr */
+
+/* For a more granular hash */
+uint32_t nexthop_hash(const struct nexthop *nexthop)
+{
+ uint32_t gate_src_rmap_raw[GATE_SIZE * 3] = {};
+ /* Get all the quick stuff */
+ uint32_t key = nexthop_hash_quick(nexthop);
+
+ assert(((sizeof(nexthop->gate) + sizeof(nexthop->src)
+ + sizeof(nexthop->rmap_src))
+ / 3)
+ == (GATE_SIZE * sizeof(uint32_t)));
+
+ memcpy(gate_src_rmap_raw, &nexthop->gate, GATE_SIZE);
+ memcpy(gate_src_rmap_raw + GATE_SIZE, &nexthop->src, GATE_SIZE);
+ memcpy(gate_src_rmap_raw + (2 * GATE_SIZE), &nexthop->rmap_src,
+ GATE_SIZE);
+
+ key = jhash2(gate_src_rmap_raw, (GATE_SIZE * 3), key);
+
return key;
}
diff --git a/lib/nexthop.h b/lib/nexthop.h
index 9dd5fc6fd3..fe029f1867 100644
--- a/lib/nexthop.h
+++ b/lib/nexthop.h
@@ -137,6 +137,14 @@ void nexthop_del_labels(struct nexthop *);
* 32-bit hash of nexthop
*/
uint32_t nexthop_hash(const struct nexthop *nexthop);
+/*
+ * Hash a nexthop only on word-sized attributes:
+ * - vrf_id
+ * - ifindex
+ * - type
+ * - (some) flags
+ */
+uint32_t nexthop_hash_quick(const struct nexthop *nexthop);
extern bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2);
extern bool nexthop_same_no_labels(const struct nexthop *nh1,
@@ -153,7 +161,9 @@ extern int nexthop_same_firsthop(struct nexthop *next1, struct nexthop *next2);
extern const char *nexthop2str(const struct nexthop *nexthop,
char *str, int size);
-extern struct nexthop *nexthop_next(struct nexthop *nexthop);
+extern struct nexthop *nexthop_next(const struct nexthop *nexthop);
+extern struct nexthop *
+nexthop_next_active_resolved(const struct nexthop *nexthop);
extern unsigned int nexthop_level(struct nexthop *nexthop);
/* Copies to an already allocated nexthop struct */
extern void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop,
diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c
index 9564321d38..9552f89568 100644
--- a/lib/nexthop_group.c
+++ b/lib/nexthop_group.c
@@ -81,6 +81,17 @@ uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg)
return num;
}
+uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg)
+{
+ struct nexthop *nhop;
+ uint8_t num = 0;
+
+ for (nhop = nhg->nexthop; nhop; nhop = nhop->next)
+ num++;
+
+ return num;
+}
+
uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg)
{
struct nexthop *nhop;
@@ -94,7 +105,22 @@ uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg)
return num;
}
-struct nexthop *nexthop_exists(struct nexthop_group *nhg, struct nexthop *nh)
+uint8_t
+nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group *nhg)
+{
+ struct nexthop *nhop;
+ uint8_t num = 0;
+
+ for (nhop = nhg->nexthop; nhop; nhop = nhop->next) {
+ if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
+ num++;
+ }
+
+ return num;
+}
+
+struct nexthop *nexthop_exists(const struct nexthop_group *nhg,
+ const struct nexthop *nh)
{
struct nexthop *nexthop;
@@ -106,6 +132,74 @@ struct nexthop *nexthop_exists(struct nexthop_group *nhg, struct nexthop *nh)
return NULL;
}
+static bool
+nexthop_group_equal_common(const struct nexthop_group *nhg1,
+ const struct nexthop_group *nhg2,
+ uint8_t (*nexthop_group_nexthop_num_func)(
+ const struct nexthop_group *nhg))
+{
+ if (nhg1 && !nhg2)
+ return false;
+
+ if (!nhg1 && nhg2)
+ return false;
+
+ if (nhg1 == nhg2)
+ return true;
+
+ if (nexthop_group_nexthop_num_func(nhg1)
+ != nexthop_group_nexthop_num_func(nhg2))
+ return false;
+
+ return true;
+}
+
+/* This assumes ordered */
+bool nexthop_group_equal_no_recurse(const struct nexthop_group *nhg1,
+ const struct nexthop_group *nhg2)
+{
+ struct nexthop *nh1 = NULL;
+ struct nexthop *nh2 = NULL;
+
+ if (!nexthop_group_equal_common(nhg1, nhg2,
+ &nexthop_group_nexthop_num_no_recurse))
+ return false;
+
+ for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2;
+ nh1 = nh1->next, nh2 = nh2->next) {
+ if (nh1 && !nh2)
+ return false;
+ if (!nh1 && nh2)
+ return false;
+ if (!nexthop_same(nh1, nh2))
+ return false;
+ }
+
+ return true;
+}
+
+/* This assumes ordered */
+bool nexthop_group_equal(const struct nexthop_group *nhg1,
+ const struct nexthop_group *nhg2)
+{
+ struct nexthop *nh1 = NULL;
+ struct nexthop *nh2 = NULL;
+
+ if (!nexthop_group_equal_common(nhg1, nhg2, &nexthop_group_nexthop_num))
+ return false;
+
+ for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2;
+ nh1 = nexthop_next(nh1), nh2 = nexthop_next(nh2)) {
+ if (nh1 && !nh2)
+ return false;
+ if (!nh1 && nh2)
+ return false;
+ if (!nexthop_same(nh1, nh2))
+ return false;
+ }
+
+ return true;
+}
struct nexthop_group *nexthop_group_new(void)
{
return XCALLOC(MTYPE_NEXTHOP_GROUP, sizeof(struct nexthop_group));
@@ -119,6 +213,9 @@ void nexthop_group_copy(struct nexthop_group *to, struct nexthop_group *from)
void nexthop_group_delete(struct nexthop_group **nhg)
{
+ if ((*nhg)->nexthop)
+ nexthops_free((*nhg)->nexthop);
+
XFREE(MTYPE_NEXTHOP_GROUP, *nhg);
}
@@ -217,7 +314,7 @@ void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh,
}
}
-uint32_t nexthop_group_hash(const struct nexthop_group *nhg)
+uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group *nhg)
{
struct nexthop *nh;
uint32_t key = 0;
@@ -232,6 +329,35 @@ uint32_t nexthop_group_hash(const struct nexthop_group *nhg)
return key;
}
+uint32_t nexthop_group_hash(const struct nexthop_group *nhg)
+{
+ struct nexthop *nh;
+ uint32_t key = 0;
+
+ for (ALL_NEXTHOPS_PTR(nhg, nh))
+ key = jhash_1word(nexthop_hash(nh), key);
+
+ return key;
+}
+
+void nexthop_group_mark_duplicates(struct nexthop_group *nhg)
+{
+ struct nexthop *nexthop, *prev;
+
+ for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE);
+ for (ALL_NEXTHOPS_PTR(nhg, prev)) {
+ if (prev == nexthop)
+ break;
+ if (nexthop_same_firsthop(nexthop, prev)) {
+ SET_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_DUPLICATE);
+ break;
+ }
+ }
+ }
+}
+
static void nhgc_delete_nexthops(struct nexthop_group_cmd *nhgc)
{
struct nexthop *nexthop;
diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h
index 4f4d40eb33..391775c69c 100644
--- a/lib/nexthop_group.h
+++ b/lib/nexthop_group.h
@@ -47,7 +47,9 @@ void nexthop_group_copy(struct nexthop_group *to,
void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh,
struct nexthop *rparent);
+uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group *nhg);
uint32_t nexthop_group_hash(const struct nexthop_group *nhg);
+void nexthop_group_mark_duplicates(struct nexthop_group *nhg);
/* The following for loop allows to iterate over the nexthop
* structure of routes.
@@ -110,8 +112,15 @@ void nexthop_group_disable_vrf(struct vrf *vrf);
void nexthop_group_interface_state_change(struct interface *ifp,
ifindex_t oldifindex);
-extern struct nexthop *nexthop_exists(struct nexthop_group *nhg,
- struct nexthop *nh);
+extern struct nexthop *nexthop_exists(const struct nexthop_group *nhg,
+ const struct nexthop *nh);
+/* This assumes ordered */
+extern bool nexthop_group_equal_no_recurse(const struct nexthop_group *nhg1,
+ const struct nexthop_group *nhg2);
+
+/* This assumes ordered */
+extern bool nexthop_group_equal(const struct nexthop_group *nhg1,
+ const struct nexthop_group *nhg2);
extern struct nexthop_group_cmd *nhgc_find(const char *name);
@@ -120,7 +129,11 @@ extern void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh);
/* Return the number of nexthops in this nhg */
extern uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg);
extern uint8_t
+nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg);
+extern uint8_t
nexthop_group_active_nexthop_num(const struct nexthop_group *nhg);
+extern uint8_t
+nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group *nhg);
#ifdef __cplusplus
}
diff --git a/lib/prefix.c b/lib/prefix.c
index 5071ca8201..e2bf3b949c 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -905,9 +905,9 @@ struct prefix_ipv4 *prefix_ipv4_new(void)
}
/* Free prefix_ipv4 structure. */
-void prefix_ipv4_free(struct prefix_ipv4 *p)
+void prefix_ipv4_free(struct prefix_ipv4 **p)
{
- prefix_free((struct prefix *)p);
+ prefix_free((struct prefix **)p);
}
/* If given string is valid return 1 else return 0 */
@@ -1077,9 +1077,9 @@ struct prefix_ipv6 *prefix_ipv6_new(void)
}
/* Free prefix for IPv6. */
-void prefix_ipv6_free(struct prefix_ipv6 *p)
+void prefix_ipv6_free(struct prefix_ipv6 **p)
{
- prefix_free((struct prefix *)p);
+ prefix_free((struct prefix **)p);
}
/* If given string is valid return 1 else return 0 */
@@ -1484,10 +1484,18 @@ struct prefix *prefix_new(void)
return p;
}
+void prefix_free_lists(void *arg)
+{
+ struct prefix *p = arg;
+
+ prefix_free(&p);
+}
+
/* Free prefix structure. */
-void prefix_free(struct prefix *p)
+void prefix_free(struct prefix **p)
{
- XFREE(MTYPE_PREFIX, p);
+ XFREE(MTYPE_PREFIX, *p);
+ *p = NULL;
}
/* Utility function to convert ipv4 prefixes to Classful prefixes */
diff --git a/lib/prefix.h b/lib/prefix.h
index 784927616a..7a93c766a3 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -396,7 +396,11 @@ extern unsigned int prefix6_bit(const struct in6_addr *prefix,
const uint16_t prefixlen);
extern struct prefix *prefix_new(void);
-extern void prefix_free(struct prefix *);
+extern void prefix_free(struct prefix **p);
+/*
+ * Function to handle prefix_free being used as a del function.
+ */
+extern void prefix_free_lists(void *arg);
extern const char *prefix_family_str(const struct prefix *);
extern int prefix_blen(const struct prefix *);
extern int str2prefix(const char *, struct prefix *);
@@ -435,7 +439,7 @@ extern void prefix2sockunion(const struct prefix *, union sockunion *);
extern int str2prefix_eth(const char *, struct prefix_eth *);
extern struct prefix_ipv4 *prefix_ipv4_new(void);
-extern void prefix_ipv4_free(struct prefix_ipv4 *);
+extern void prefix_ipv4_free(struct prefix_ipv4 **p);
extern int str2prefix_ipv4(const char *, struct prefix_ipv4 *);
extern void apply_mask_ipv4(struct prefix_ipv4 *);
@@ -460,7 +464,7 @@ extern in_addr_t ipv4_broadcast_addr(in_addr_t hostaddr, int masklen);
extern int netmask_str2prefix_str(const char *, const char *, char *);
extern struct prefix_ipv6 *prefix_ipv6_new(void);
-extern void prefix_ipv6_free(struct prefix_ipv6 *);
+extern void prefix_ipv6_free(struct prefix_ipv6 **p);
extern int str2prefix_ipv6(const char *, struct prefix_ipv6 *);
extern void apply_mask_ipv6(struct prefix_ipv6 *);
diff --git a/lib/printf/glue.c b/lib/printf/glue.c
index 1b760dc2d3..29ca26ad5d 100644
--- a/lib/printf/glue.c
+++ b/lib/printf/glue.c
@@ -101,9 +101,11 @@ char *vasnprintfrr(struct memtype *mt, char *out, size_t outsz, const char *fmt,
va_copy(ap2, ap);
len = vbprintfrr(&fb, fmt, ap);
- if (len < 0)
+ if (len < 0) {
+ va_end(ap2);
/* error = malformed format string => try something useful */
return qstrdup(mt, fmt);
+ }
if ((size_t)len >= outsz - 1) {
ret = qmalloc(mt, len + 1);
@@ -112,6 +114,8 @@ char *vasnprintfrr(struct memtype *mt, char *out, size_t outsz, const char *fmt,
vbprintfrr(&fb, fmt, ap2);
}
+
+ va_end(ap2);
ret[len] = '\0';
return ret;
}
diff --git a/lib/route_types.txt b/lib/route_types.txt
index 59f3a91cf0..71d0a46449 100644
--- a/lib/route_types.txt
+++ b/lib/route_types.txt
@@ -84,6 +84,7 @@ ZEBRA_ROUTE_PBR, pbr, pbrd, 'F', 1, 1, 0, "PBR"
ZEBRA_ROUTE_BFD, bfd, bfdd, '-', 0, 0, 0, "BFD"
ZEBRA_ROUTE_OPENFABRIC, openfabric, fabricd, 'f', 1, 1, 1, "OpenFabric"
ZEBRA_ROUTE_VRRP, vrrp, vrrpd, '-', 0, 0, 0, "VRRP"
+ZEBRA_ROUTE_NHG, nhg, none, '-', 0, 0, 0, "Nexthop Group"
ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-"
@@ -113,3 +114,4 @@ ZEBRA_ROUTE_PBR, "Policy Based Routing (PBR)"
ZEBRA_ROUTE_BFD, "Bidirectional Fowarding Detection (BFD)"
ZEBRA_ROUTE_VRRP, "Virtual Router Redundancy Protocol (VRRP)"
ZEBRA_ROUTE_OPENFABRIC, "OpenFabric Routing Protocol"
+ZEBRA_ROUTE_NHG, "Zebra Nexthop Groups (NHG)"
diff --git a/lib/zebra.h b/lib/zebra.h
index b17ef700b4..de9a347e1f 100644
--- a/lib/zebra.h
+++ b/lib/zebra.h
@@ -52,10 +52,9 @@ typedef unsigned char uint8_t;
#include <sys/types.h>
#include <sys/param.h>
#ifdef HAVE_SYS_SYSCTL_H
-#ifdef GNU_LINUX
-#include <linux/types.h>
-#endif
+#ifndef GNU_LINUX
#include <sys/sysctl.h>
+#endif
#endif /* HAVE_SYS_SYSCTL_H */
#include <sys/ioctl.h>
#ifdef HAVE_SYS_CONF_H
diff --git a/nhrpd/nhrp_interface.c b/nhrpd/nhrp_interface.c
index e4f614c7c4..9f828a1c7d 100644
--- a/nhrpd/nhrp_interface.c
+++ b/nhrpd/nhrp_interface.c
@@ -364,7 +364,7 @@ int nhrp_interface_address_delete(ZAPI_CALLBACK_ARGS)
nhrp_interface_update_address(
ifc->ifp, family2afi(PREFIX_FAMILY(ifc->address)), 0);
- connected_free(ifc);
+ connected_free(&ifc);
return 0;
}
diff --git a/nhrpd/nhrp_vty.c b/nhrpd/nhrp_vty.c
index cfedc1c6b9..ba8c5d4953 100644
--- a/nhrpd/nhrp_vty.c
+++ b/nhrpd/nhrp_vty.c
@@ -523,7 +523,7 @@ DEFUN(if_no_nhrp_map, if_no_nhrp_map_cmd,
{
VTY_DECLVAR_CONTEXT(interface, ifp);
afi_t afi = cmd_to_afi(argv[1]);
- union sockunion proto_addr;
+ union sockunion proto_addr, nbma_addr;
struct nhrp_cache *c;
if (str2sockunion(argv[4]->arg, &proto_addr) < 0
@@ -534,7 +534,8 @@ DEFUN(if_no_nhrp_map, if_no_nhrp_map_cmd,
if (!c || !c->map)
return nhrp_vty_return(vty, NHRP_ERR_ENTRY_NOT_FOUND);
- nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL);
+ nhrp_cache_update_binding(c, c->cur.type, -1,
+ nhrp_peer_get(ifp, &nbma_addr), 0, NULL);
return CMD_SUCCESS;
}
diff --git a/ospf6d/ospf6_bfd.c b/ospf6d/ospf6_bfd.c
index f0500601b0..4e7a0050aa 100644
--- a/ospf6d/ospf6_bfd.c
+++ b/ospf6d/ospf6_bfd.c
@@ -236,7 +236,7 @@ static int ospf6_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS)
continue;
old_status = bfd_info->status;
- bfd_info->status = status;
+ BFD_SET_CLIENT_STATUS(bfd_info->status, status);
monotime(&tv);
bfd_info->last_update = tv.tv_sec;
diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c
index d8a6a39e1e..6832737ada 100644
--- a/ospf6d/ospf6_zebra.c
+++ b/ospf6d/ospf6_zebra.c
@@ -143,7 +143,7 @@ static int ospf6_zebra_if_address_update_delete(ZAPI_CALLBACK_ARGS)
ospf6_interface_state_update(c->ifp);
}
- connected_free(c);
+ connected_free(&c);
return 0;
}
diff --git a/ospfd/ospf_bfd.c b/ospfd/ospf_bfd.c
index a17975270a..b9e78f4cd3 100644
--- a/ospfd/ospf_bfd.c
+++ b/ospfd/ospf_bfd.c
@@ -202,8 +202,9 @@ static int ospf_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS)
struct interface *ifp;
struct ospf_interface *oi;
struct ospf_if_params *params;
- struct ospf_neighbor *nbr;
+ struct ospf_neighbor *nbr = NULL;
struct route_node *node;
+ struct route_node *n_node;
struct prefix p;
int status;
int old_status;
@@ -231,7 +232,28 @@ static int ospf_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS)
if ((oi = node->info) == NULL)
continue;
- nbr = ospf_nbr_lookup_by_addr(oi->nbrs, &p.u.prefix4);
+ /* walk the neighbor list for point-to-point network */
+ if (oi->type == OSPF_IFTYPE_POINTOPOINT) {
+ for (n_node = route_top(oi->nbrs); n_node;
+ n_node = route_next(n_node)) {
+ nbr = n_node->info;
+ if (nbr) {
+ /* skip myself */
+ if (nbr == oi->nbr_self) {
+ nbr = NULL;
+ continue;
+ }
+
+ /* Found the matching neighbor */
+ if (nbr->src.s_addr ==
+ p.u.prefix4.s_addr)
+ break;
+ }
+ }
+ } else {
+ nbr = ospf_nbr_lookup_by_addr(oi->nbrs, &p.u.prefix4);
+ }
+
if (!nbr || !nbr->bfd_info)
continue;
@@ -240,7 +262,7 @@ static int ospf_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS)
continue;
old_status = bfd_info->status;
- bfd_info->status = status;
+ BFD_SET_CLIENT_STATUS(bfd_info->status, status);
monotime(&tv);
bfd_info->last_update = tv.tv_sec;
diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c
index 1f5e0da944..5459e3b87c 100644
--- a/ospfd/ospf_interface.c
+++ b/ospfd/ospf_interface.c
@@ -902,11 +902,10 @@ struct ospf_interface *ospf_vl_new(struct ospf *ospf,
static void ospf_vl_if_delete(struct ospf_vl_data *vl_data)
{
- struct interface *ifp = vl_data->vl_oi->ifp;
vl_data->vl_oi->address->u.prefix4.s_addr = 0;
vl_data->vl_oi->address->prefixlen = 0;
ospf_if_free(vl_data->vl_oi);
- if_delete(ifp);
+ if_delete(&vl_data->vl_oi->ifp);
vlink_count--;
}
diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c
index 5678d545ba..68d9d3bf83 100644
--- a/ospfd/ospf_zebra.c
+++ b/ospfd/ospf_zebra.c
@@ -150,7 +150,7 @@ static int ospf_interface_address_delete(ZAPI_CALLBACK_ARGS)
rn = route_node_lookup(IF_OIFS(ifp), &p);
if (!rn) {
- connected_free(c);
+ connected_free(&c);
return 0;
}
@@ -163,7 +163,7 @@ static int ospf_interface_address_delete(ZAPI_CALLBACK_ARGS)
ospf_if_interface(c->ifp);
- connected_free(c);
+ connected_free(&c);
return 0;
}
diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c
index 67a1fe2ffe..7ccd14d1f1 100644
--- a/pbrd/pbr_nht.c
+++ b/pbrd/pbr_nht.c
@@ -578,8 +578,6 @@ void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms)
hash_release(pbr_nhg_hash, pnhgc);
- _nexthop_del(pbrms->nhg, nh);
- nexthop_free(nh);
nexthop_group_delete(&pbrms->nhg);
XFREE(MTYPE_TMP, pbrms->internal_nhg_name);
}
@@ -639,7 +637,6 @@ void pbr_nht_delete_group(const char *name)
if (pbrms->nhgrp_name
&& strmatch(pbrms->nhgrp_name, name)) {
pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
- nexthop_group_delete(&pbrms->nhg);
pbrms->nhg = NULL;
pbrms->internal_nhg_name = NULL;
pbrm->valid = false;
diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c
index 069b3e6c9b..e0fd147b0e 100644
--- a/pbrd/pbr_vty.c
+++ b/pbrd/pbr_vty.c
@@ -133,10 +133,8 @@ DEFPY(pbr_map_match_src, pbr_map_match_src_cmd,
if (!pbrms->src)
pbrms->src = prefix_new();
prefix_copy(pbrms->src, prefix);
- } else {
- prefix_free(pbrms->src);
- pbrms->src = 0;
- }
+ } else
+ prefix_free(&pbrms->src);
pbr_map_check(pbrms);
@@ -162,10 +160,8 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd,
if (!pbrms->dst)
pbrms->dst = prefix_new();
prefix_copy(pbrms->dst, prefix);
- } else {
- prefix_free(pbrms->dst);
- pbrms->dst = NULL;
- }
+ } else
+ prefix_free(&pbrms->dst);
pbr_map_check(pbrms);
diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c
index 39e92467ab..719374e3b9 100644
--- a/pbrd/pbr_zebra.c
+++ b/pbrd/pbr_zebra.c
@@ -109,7 +109,7 @@ static int interface_address_delete(ZAPI_CALLBACK_ARGS)
"%s: %s deleted %s", __PRETTY_FUNCTION__, c->ifp->name,
prefix2str(c->address, buf, sizeof(buf)));
- connected_free(c);
+ connected_free(&c);
return 0;
}
diff --git a/pimd/pim_bfd.c b/pimd/pim_bfd.c
index 87d0f9fa22..01a7980858 100644
--- a/pimd/pim_bfd.c
+++ b/pimd/pim_bfd.c
@@ -270,7 +270,7 @@ static int pim_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS)
continue;
}
old_status = bfd_info->status;
- bfd_info->status = status;
+ BFD_SET_CLIENT_STATUS(bfd_info->status, status);
monotime(&tv);
bfd_info->last_update = tv.tv_sec;
diff --git a/pimd/pim_bsm.c b/pimd/pim_bsm.c
index 675092dbec..1383e3db11 100644
--- a/pimd/pim_bsm.c
+++ b/pimd/pim_bsm.c
@@ -290,8 +290,7 @@ void pim_bsm_proc_free(struct pim_instance *pim)
pim_free_bsgrp_data(bsgrp);
}
- if (pim->global_scope.bsrp_table)
- route_table_finish(pim->global_scope.bsrp_table);
+ route_table_finish(pim->global_scope.bsrp_table);
}
static bool is_hold_time_elapsed(void *data)
@@ -683,8 +682,13 @@ static bool pim_bsm_send_intf(uint8_t *buf, int len, struct interface *ifp,
return false;
}
- pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address, dst_addr,
- buf, len, ifp->name);
+ if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address,
+ dst_addr, buf, len, ifp->name)) {
+ zlog_warn("%s: Could not send BSM message on interface: %s",
+ __PRETTY_FUNCTION__, ifp->name);
+ return false;
+ }
+
pim_ifp->pim_ifstat_bsm_tx++;
pim_ifp->pim->bsm_sent++;
return true;
@@ -1028,7 +1032,8 @@ static uint32_t hash_calc_on_grp_rp(struct prefix group, struct in_addr rp,
else
grpaddr = grpaddr & mask;
rp_add = ntohl(rp.s_addr);
- temp = 1103515245 * ((1103515245 * grpaddr + 12345) ^ rp_add) + 12345;
+ temp = 1103515245 * ((1103515245 * (uint64_t)grpaddr + 12345) ^ rp_add)
+ + 12345;
hash = temp & (0x7fffffff);
return hash;
}
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index 28b4af9457..e5686a94f4 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -5996,12 +5996,6 @@ static int pim_rp_cmd_worker(struct pim_instance *pim, struct vty *vty,
return CMD_WARNING_CONFIG_FAILED;
}
- if (result == PIM_GROUP_BAD_ADDR_MASK_COMBO) {
- vty_out(vty, "%% Inconsistent address and mask: %s\n",
- group);
- return CMD_WARNING_CONFIG_FAILED;
- }
-
return CMD_SUCCESS;
}
@@ -6347,6 +6341,7 @@ static int pim_ssm_cmd_worker(struct pim_instance *pim, struct vty *vty,
const char *plist)
{
int result = pim_ssm_range_set(pim, pim->vrf_id, plist);
+ int ret = CMD_WARNING_CONFIG_FAILED;
if (result == PIM_SSM_ERR_NONE)
return CMD_SUCCESS;
@@ -6357,12 +6352,13 @@ static int pim_ssm_cmd_worker(struct pim_instance *pim, struct vty *vty,
break;
case PIM_SSM_ERR_DUP:
vty_out(vty, "%% duplicate config\n");
+ ret = CMD_WARNING;
break;
default:
vty_out(vty, "%% ssm range config failed\n");
}
- return CMD_WARNING_CONFIG_FAILED;
+ return ret;
}
DEFUN (ip_pim_ssm_prefix_list,
diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c
index 66e64cefa0..4ca38f10d1 100644
--- a/pimd/pim_ifchannel.c
+++ b/pimd/pim_ifchannel.c
@@ -191,11 +191,13 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch)
pim_upstream_del(pim_ifp->pim, ch->upstream,
__PRETTY_FUNCTION__);
- else
- zlog_warn("%s: Avoiding deletion of upstream with ref_count %d "
- "from ifchannel(%s): %s", __PRETTY_FUNCTION__,
- ch->upstream->ref_count, ch->interface->name,
- ch->sg_str);
+ else {
+ if (PIM_DEBUG_PIM_TRACE)
+ zlog_debug("%s: Avoiding deletion of upstream with ref_count %d "
+ "from ifchannel(%s): %s", __PRETTY_FUNCTION__,
+ ch->upstream->ref_count, ch->interface->name,
+ ch->sg_str);
+ }
ch->upstream = NULL;
diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c
index 722ecb2a72..3f2aaf2bbe 100644
--- a/pimd/pim_neighbor.c
+++ b/pimd/pim_neighbor.c
@@ -767,7 +767,7 @@ static void delete_from_neigh_addr(struct interface *ifp,
other_neigh_str, ifp->name);
listnode_delete(neigh->prefix_list, p);
- prefix_free(p);
+ prefix_free(&p);
}
}
diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c
index 35c040c64c..5542db27c9 100644
--- a/pimd/pim_rp.c
+++ b/pimd/pim_rp.c
@@ -922,10 +922,11 @@ int pim_rp_change(struct pim_instance *pim, struct in_addr new_rp_addr,
}
}
+ nht_p.family = AF_INET;
+ nht_p.prefixlen = IPV4_MAX_BITLEN;
+
/* Deregister old RP addr with Zebra NHT */
if (rp_info->rp.rpf_addr.u.prefix4.s_addr != INADDR_ANY) {
- nht_p.family = AF_INET;
- nht_p.prefixlen = IPV4_MAX_BITLEN;
nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4;
if (PIM_DEBUG_PIM_NHT_RP) {
char buf[PREFIX2STR_BUFFER];
diff --git a/pimd/pim_tlv.c b/pimd/pim_tlv.c
index d93a360448..4fe3237395 100644
--- a/pimd/pim_tlv.c
+++ b/pimd/pim_tlv.c
@@ -757,8 +757,7 @@ int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr,
*/
if (!*hello_option_addr_list) {
*hello_option_addr_list = list_new();
- (*hello_option_addr_list)->del =
- (void (*)(void *))prefix_free;
+ (*hello_option_addr_list)->del = prefix_free_lists;
}
/*
diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c
index dadcbbe65d..dfddee99d0 100644
--- a/pimd/pim_zebra.c
+++ b/pimd/pim_zebra.c
@@ -233,7 +233,7 @@ static int pim_zebra_if_address_del(ZAPI_CALLBACK_ARGS)
pim_i_am_rp_re_evaluate(pim);
}
- connected_free(c);
+ connected_free(&c);
return 0;
}
diff --git a/ripd/rip_cli.c b/ripd/rip_cli.c
index 627d3d1993..5c26c0cef7 100644
--- a/ripd/rip_cli.c
+++ b/ripd/rip_cli.c
@@ -29,7 +29,7 @@
#include "libfrr.h"
#include "ripd/ripd.h"
-#include "ripd/rip_cli.h"
+#include "ripd/rip_nb.h"
#ifndef VTYSH_EXTRACT_PL
#include "ripd/rip_cli_clippy.c"
#endif
diff --git a/ripd/rip_cli.h b/ripd/rip_cli.h
deleted file mode 100644
index ef1e1504e8..0000000000
--- a/ripd/rip_cli.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
- * Copyright (C) 2018 NetDEF, Inc.
- * Renato Westphal
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; see the file COPYING; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef _FRR_RIP_CLI_H_
-#define _FRR_RIP_CLI_H_
-
-extern void cli_show_router_rip(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_rip_allow_ecmp(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_rip_default_information_originate(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_rip_default_metric(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_rip_distance(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_rip_distance_source(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_rip_neighbor(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_rip_network_prefix(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_rip_network_interface(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_rip_offset_list(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_rip_passive_default(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_rip_passive_interface(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_rip_non_passive_interface(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_rip_redistribute(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_rip_route(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_rip_timers(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_rip_version(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_ip_rip_split_horizon(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_ip_rip_v2_broadcast(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_ip_rip_receive_version(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_ip_rip_send_version(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_ip_rip_authentication_scheme(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_ip_rip_authentication_string(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_ip_rip_authentication_key_chain(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-
-#endif /* _FRR_RIP_CLI_H_ */
diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c
index 3173277ba7..4d48740606 100644
--- a/ripd/rip_interface.c
+++ b/ripd/rip_interface.c
@@ -648,7 +648,7 @@ int rip_interface_address_delete(ZAPI_CALLBACK_ARGS)
rip_apply_address_del(ifc);
}
- connected_free(ifc);
+ connected_free(&ifc);
}
return 0;
diff --git a/ripd/rip_main.c b/ripd/rip_main.c
index 773cb1d0fe..08cc515856 100644
--- a/ripd/rip_main.c
+++ b/ripd/rip_main.c
@@ -38,6 +38,7 @@
#include "libfrr.h"
#include "ripd/ripd.h"
+#include "ripd/rip_nb.h"
#include "ripd/rip_errors.h"
/* ripd options. */
diff --git a/ripd/rip_nb.c b/ripd/rip_nb.c
new file mode 100644
index 0000000000..4716041ad6
--- /dev/null
+++ b/ripd/rip_nb.c
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "northbound.h"
+#include "libfrr.h"
+
+#include "ripd/rip_nb.h"
+
+/* clang-format off */
+const struct frr_yang_module_info frr_ripd_info = {
+ .name = "frr-ripd",
+ .nodes = {
+ {
+ .xpath = "/frr-ripd:ripd/instance",
+ .cbs = {
+ .cli_show = cli_show_router_rip,
+ .create = ripd_instance_create,
+ .destroy = ripd_instance_destroy,
+ .get_keys = ripd_instance_get_keys,
+ .get_next = ripd_instance_get_next,
+ .lookup_entry = ripd_instance_lookup_entry,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/allow-ecmp",
+ .cbs = {
+ .cli_show = cli_show_rip_allow_ecmp,
+ .modify = ripd_instance_allow_ecmp_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/default-information-originate",
+ .cbs = {
+ .cli_show = cli_show_rip_default_information_originate,
+ .modify = ripd_instance_default_information_originate_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/default-metric",
+ .cbs = {
+ .cli_show = cli_show_rip_default_metric,
+ .modify = ripd_instance_default_metric_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/distance/default",
+ .cbs = {
+ .cli_show = cli_show_rip_distance,
+ .modify = ripd_instance_distance_default_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/distance/source",
+ .cbs = {
+ .cli_show = cli_show_rip_distance_source,
+ .create = ripd_instance_distance_source_create,
+ .destroy = ripd_instance_distance_source_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/distance/source/distance",
+ .cbs = {
+ .modify = ripd_instance_distance_source_distance_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/distance/source/access-list",
+ .cbs = {
+ .destroy = ripd_instance_distance_source_access_list_destroy,
+ .modify = ripd_instance_distance_source_access_list_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/explicit-neighbor",
+ .cbs = {
+ .cli_show = cli_show_rip_neighbor,
+ .create = ripd_instance_explicit_neighbor_create,
+ .destroy = ripd_instance_explicit_neighbor_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/network",
+ .cbs = {
+ .cli_show = cli_show_rip_network_prefix,
+ .create = ripd_instance_network_create,
+ .destroy = ripd_instance_network_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/interface",
+ .cbs = {
+ .cli_show = cli_show_rip_network_interface,
+ .create = ripd_instance_interface_create,
+ .destroy = ripd_instance_interface_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/offset-list",
+ .cbs = {
+ .cli_show = cli_show_rip_offset_list,
+ .create = ripd_instance_offset_list_create,
+ .destroy = ripd_instance_offset_list_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/offset-list/access-list",
+ .cbs = {
+ .modify = ripd_instance_offset_list_access_list_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/offset-list/metric",
+ .cbs = {
+ .modify = ripd_instance_offset_list_metric_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/passive-default",
+ .cbs = {
+ .cli_show = cli_show_rip_passive_default,
+ .modify = ripd_instance_passive_default_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/passive-interface",
+ .cbs = {
+ .cli_show = cli_show_rip_passive_interface,
+ .create = ripd_instance_passive_interface_create,
+ .destroy = ripd_instance_passive_interface_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/non-passive-interface",
+ .cbs = {
+ .cli_show = cli_show_rip_non_passive_interface,
+ .create = ripd_instance_non_passive_interface_create,
+ .destroy = ripd_instance_non_passive_interface_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/redistribute",
+ .cbs = {
+ .apply_finish = ripd_instance_redistribute_apply_finish,
+ .cli_show = cli_show_rip_redistribute,
+ .create = ripd_instance_redistribute_create,
+ .destroy = ripd_instance_redistribute_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/redistribute/route-map",
+ .cbs = {
+ .destroy = ripd_instance_redistribute_route_map_destroy,
+ .modify = ripd_instance_redistribute_route_map_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/redistribute/metric",
+ .cbs = {
+ .destroy = ripd_instance_redistribute_metric_destroy,
+ .modify = ripd_instance_redistribute_metric_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/static-route",
+ .cbs = {
+ .cli_show = cli_show_rip_route,
+ .create = ripd_instance_static_route_create,
+ .destroy = ripd_instance_static_route_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/timers",
+ .cbs = {
+ .apply_finish = ripd_instance_timers_apply_finish,
+ .cli_show = cli_show_rip_timers,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/timers/flush-interval",
+ .cbs = {
+ .modify = ripd_instance_timers_flush_interval_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/timers/holddown-interval",
+ .cbs = {
+ .modify = ripd_instance_timers_holddown_interval_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/timers/update-interval",
+ .cbs = {
+ .modify = ripd_instance_timers_update_interval_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/version",
+ .cbs = {
+ .cli_show = cli_show_rip_version,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/version/receive",
+ .cbs = {
+ .modify = ripd_instance_version_receive_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/version/send",
+ .cbs = {
+ .modify = ripd_instance_version_send_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-ripd:rip/split-horizon",
+ .cbs = {
+ .cli_show = cli_show_ip_rip_split_horizon,
+ .modify = lib_interface_rip_split_horizon_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-ripd:rip/v2-broadcast",
+ .cbs = {
+ .cli_show = cli_show_ip_rip_v2_broadcast,
+ .modify = lib_interface_rip_v2_broadcast_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-ripd:rip/version-receive",
+ .cbs = {
+ .cli_show = cli_show_ip_rip_receive_version,
+ .modify = lib_interface_rip_version_receive_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-ripd:rip/version-send",
+ .cbs = {
+ .cli_show = cli_show_ip_rip_send_version,
+ .modify = lib_interface_rip_version_send_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-scheme",
+ .cbs = {
+ .cli_show = cli_show_ip_rip_authentication_scheme,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-scheme/mode",
+ .cbs = {
+ .modify = lib_interface_rip_authentication_scheme_mode_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-scheme/md5-auth-length",
+ .cbs = {
+ .destroy = lib_interface_rip_authentication_scheme_md5_auth_length_destroy,
+ .modify = lib_interface_rip_authentication_scheme_md5_auth_length_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-password",
+ .cbs = {
+ .cli_show = cli_show_ip_rip_authentication_string,
+ .destroy = lib_interface_rip_authentication_password_destroy,
+ .modify = lib_interface_rip_authentication_password_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-key-chain",
+ .cbs = {
+ .cli_show = cli_show_ip_rip_authentication_key_chain,
+ .destroy = lib_interface_rip_authentication_key_chain_destroy,
+ .modify = lib_interface_rip_authentication_key_chain_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor",
+ .cbs = {
+ .get_keys = ripd_instance_state_neighbors_neighbor_get_keys,
+ .get_next = ripd_instance_state_neighbors_neighbor_get_next,
+ .lookup_entry = ripd_instance_state_neighbors_neighbor_lookup_entry,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor/address",
+ .cbs = {
+ .get_elem = ripd_instance_state_neighbors_neighbor_address_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor/last-update",
+ .cbs = {
+ .get_elem = ripd_instance_state_neighbors_neighbor_last_update_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor/bad-packets-rcvd",
+ .cbs = {
+ .get_elem = ripd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor/bad-routes-rcvd",
+ .cbs = {
+ .get_elem = ripd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/state/routes/route",
+ .cbs = {
+ .get_keys = ripd_instance_state_routes_route_get_keys,
+ .get_next = ripd_instance_state_routes_route_get_next,
+ .lookup_entry = ripd_instance_state_routes_route_lookup_entry,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/state/routes/route/prefix",
+ .cbs = {
+ .get_elem = ripd_instance_state_routes_route_prefix_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/state/routes/route/next-hop",
+ .cbs = {
+ .get_elem = ripd_instance_state_routes_route_next_hop_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/state/routes/route/interface",
+ .cbs = {
+ .get_elem = ripd_instance_state_routes_route_interface_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/state/routes/route/metric",
+ .cbs = {
+ .get_elem = ripd_instance_state_routes_route_metric_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripd:clear-rip-route",
+ .cbs = {
+ .rpc = clear_rip_route_rpc,
+ },
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
diff --git a/ripd/rip_nb.h b/ripd/rip_nb.h
new file mode 100644
index 0000000000..441b253fea
--- /dev/null
+++ b/ripd/rip_nb.h
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_RIP_NB_H_
+#define _FRR_RIP_NB_H_
+
+extern const struct frr_yang_module_info frr_ripd_info;
+
+/* Mandatory callbacks. */
+int ripd_instance_create(enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_destroy(enum nb_event event, const struct lyd_node *dnode);
+const void *ripd_instance_get_next(const void *parent_list_entry,
+ const void *list_entry);
+int ripd_instance_get_keys(const void *list_entry, struct yang_list_keys *keys);
+const void *ripd_instance_lookup_entry(const void *parent_list_entry,
+ const struct yang_list_keys *keys);
+int ripd_instance_allow_ecmp_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_default_information_originate_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_default_metric_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_distance_default_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_distance_source_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_distance_source_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int ripd_instance_distance_source_distance_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_distance_source_access_list_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_distance_source_access_list_destroy(
+ enum nb_event event, const struct lyd_node *dnode);
+int ripd_instance_explicit_neighbor_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_explicit_neighbor_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int ripd_instance_network_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_network_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int ripd_instance_interface_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_interface_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int ripd_instance_offset_list_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_offset_list_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int ripd_instance_offset_list_access_list_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_offset_list_metric_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_passive_default_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_passive_interface_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_passive_interface_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int ripd_instance_non_passive_interface_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_non_passive_interface_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int ripd_instance_redistribute_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_redistribute_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int ripd_instance_redistribute_route_map_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_redistribute_route_map_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int ripd_instance_redistribute_metric_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_redistribute_metric_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int ripd_instance_static_route_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_static_route_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int ripd_instance_timers_flush_interval_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_timers_holddown_interval_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_timers_update_interval_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_version_receive_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripd_instance_version_send_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+const void *
+ripd_instance_state_neighbors_neighbor_get_next(const void *parent_list_entry,
+ const void *list_entry);
+int ripd_instance_state_neighbors_neighbor_get_keys(
+ const void *list_entry, struct yang_list_keys *keys);
+const void *ripd_instance_state_neighbors_neighbor_lookup_entry(
+ const void *parent_list_entry, const struct yang_list_keys *keys);
+struct yang_data *
+ripd_instance_state_neighbors_neighbor_address_get_elem(const char *xpath,
+ const void *list_entry);
+struct yang_data *ripd_instance_state_neighbors_neighbor_last_update_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+ripd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+ripd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem(
+ const char *xpath, const void *list_entry);
+const void *
+ripd_instance_state_routes_route_get_next(const void *parent_list_entry,
+ const void *list_entry);
+int ripd_instance_state_routes_route_get_keys(const void *list_entry,
+ struct yang_list_keys *keys);
+const void *ripd_instance_state_routes_route_lookup_entry(
+ const void *parent_list_entry, const struct yang_list_keys *keys);
+struct yang_data *
+ripd_instance_state_routes_route_prefix_get_elem(const char *xpath,
+ const void *list_entry);
+struct yang_data *
+ripd_instance_state_routes_route_next_hop_get_elem(const char *xpath,
+ const void *list_entry);
+struct yang_data *
+ripd_instance_state_routes_route_interface_get_elem(const char *xpath,
+ const void *list_entry);
+struct yang_data *
+ripd_instance_state_routes_route_metric_get_elem(const char *xpath,
+ const void *list_entry);
+int clear_rip_route_rpc(const char *xpath, const struct list *input,
+ struct list *output);
+int lib_interface_rip_split_horizon_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_rip_v2_broadcast_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_rip_version_receive_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_rip_version_send_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_rip_authentication_scheme_mode_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_rip_authentication_scheme_md5_auth_length_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_rip_authentication_scheme_md5_auth_length_destroy(
+ enum nb_event event, const struct lyd_node *dnode);
+int lib_interface_rip_authentication_password_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_rip_authentication_password_destroy(
+ enum nb_event event, const struct lyd_node *dnode);
+int lib_interface_rip_authentication_key_chain_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int lib_interface_rip_authentication_key_chain_destroy(
+ enum nb_event event, const struct lyd_node *dnode);
+
+/* Optional 'apply_finish' callbacks. */
+void ripd_instance_redistribute_apply_finish(const struct lyd_node *dnode);
+void ripd_instance_timers_apply_finish(const struct lyd_node *dnode);
+
+/* Optional 'cli_show' callbacks. */
+void cli_show_router_rip(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_allow_ecmp(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_default_information_originate(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_default_metric(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_distance(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_distance_source(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_neighbor(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_network_prefix(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_network_interface(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_offset_list(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_passive_default(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_passive_interface(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_non_passive_interface(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_redistribute(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_route(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_timers(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_version(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_rip_split_horizon(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_rip_v2_broadcast(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_rip_receive_version(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_rip_send_version(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_rip_authentication_scheme(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_rip_authentication_string(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_rip_authentication_key_chain(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults);
+
+/* Notifications. */
+extern void ripd_notif_send_auth_type_failure(const char *ifname);
+extern void ripd_notif_send_auth_failure(const char *ifname);
+
+#endif /* _FRR_RIP_NB_H_ */
diff --git a/ripd/rip_nb_config.c b/ripd/rip_nb_config.c
new file mode 100644
index 0000000000..e75db342af
--- /dev/null
+++ b/ripd/rip_nb_config.c
@@ -0,0 +1,1099 @@
+/*
+ * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "vrf.h"
+#include "log.h"
+#include "prefix.h"
+#include "table.h"
+#include "command.h"
+#include "routemap.h"
+#include "northbound.h"
+#include "libfrr.h"
+
+#include "ripd/ripd.h"
+#include "ripd/rip_nb.h"
+#include "ripd/rip_debug.h"
+#include "ripd/rip_interface.h"
+
+/*
+ * XPath: /frr-ripd:ripd/instance
+ */
+int ripd_instance_create(enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+ struct vrf *vrf;
+ const char *vrf_name;
+ int socket;
+
+ vrf_name = yang_dnode_get_string(dnode, "./vrf");
+ vrf = vrf_lookup_by_name(vrf_name);
+
+ /*
+ * Try to create a RIP socket only if the VRF is enabled, otherwise
+ * create a disabled RIP instance and wait for the VRF to be enabled.
+ */
+ switch (event) {
+ case NB_EV_VALIDATE:
+ break;
+ case NB_EV_PREPARE:
+ if (!vrf || !vrf_is_enabled(vrf))
+ break;
+
+ socket = rip_create_socket(vrf);
+ if (socket < 0)
+ return NB_ERR_RESOURCE;
+ resource->fd = socket;
+ break;
+ case NB_EV_ABORT:
+ if (!vrf || !vrf_is_enabled(vrf))
+ break;
+
+ socket = resource->fd;
+ close(socket);
+ break;
+ case NB_EV_APPLY:
+ if (vrf && vrf_is_enabled(vrf))
+ socket = resource->fd;
+ else
+ socket = -1;
+
+ rip = rip_create(vrf_name, vrf, socket);
+ nb_running_set_entry(dnode, rip);
+ break;
+ }
+
+ return NB_OK;
+}
+
+int ripd_instance_destroy(enum nb_event event, const struct lyd_node *dnode)
+{
+ struct rip *rip;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_unset_entry(dnode);
+ rip_clean(rip);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/allow-ecmp
+ */
+int ripd_instance_allow_ecmp_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ rip->ecmp = yang_dnode_get_bool(dnode, NULL);
+ if (!rip->ecmp)
+ rip_ecmp_disable(rip);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/default-information-originate
+ */
+int ripd_instance_default_information_originate_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+ bool default_information;
+ struct prefix_ipv4 p;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ default_information = yang_dnode_get_bool(dnode, NULL);
+
+ memset(&p, 0, sizeof(struct prefix_ipv4));
+ p.family = AF_INET;
+ if (default_information) {
+ struct nexthop nh;
+
+ memset(&nh, 0, sizeof(nh));
+ nh.type = NEXTHOP_TYPE_IPV4;
+ rip_redistribute_add(rip, ZEBRA_ROUTE_RIP, RIP_ROUTE_DEFAULT,
+ &p, &nh, 0, 0, 0);
+ } else {
+ rip_redistribute_delete(rip, ZEBRA_ROUTE_RIP, RIP_ROUTE_DEFAULT,
+ &p, 0);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/default-metric
+ */
+int ripd_instance_default_metric_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ rip->default_metric = yang_dnode_get_uint8(dnode, NULL);
+ /* rip_update_default_metric (); */
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/distance/default
+ */
+int ripd_instance_distance_default_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ rip->distance = yang_dnode_get_uint8(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/distance/source
+ */
+int ripd_instance_distance_source_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+ struct prefix_ipv4 prefix;
+ struct route_node *rn;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ yang_dnode_get_ipv4p(&prefix, dnode, "./prefix");
+ apply_mask_ipv4(&prefix);
+
+ /* Get RIP distance node. */
+ rip = nb_running_get_entry(dnode, NULL, true);
+ rn = route_node_get(rip->distance_table, (struct prefix *)&prefix);
+ rn->info = rip_distance_new();
+ nb_running_set_entry(dnode, rn);
+
+ return NB_OK;
+}
+
+int ripd_instance_distance_source_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct route_node *rn;
+ struct rip_distance *rdistance;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rn = nb_running_unset_entry(dnode);
+ rdistance = rn->info;
+ rip_distance_free(rdistance);
+ rn->info = NULL;
+ route_unlock_node(rn);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/distance/source/distance
+ */
+int ripd_instance_distance_source_distance_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct route_node *rn;
+ uint8_t distance;
+ struct rip_distance *rdistance;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ /* Set distance value. */
+ rn = nb_running_get_entry(dnode, NULL, true);
+ distance = yang_dnode_get_uint8(dnode, NULL);
+ rdistance = rn->info;
+ rdistance->distance = distance;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/distance/source/access-list
+ */
+int ripd_instance_distance_source_access_list_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ const char *acl_name;
+ struct route_node *rn;
+ struct rip_distance *rdistance;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ acl_name = yang_dnode_get_string(dnode, NULL);
+
+ /* Set access-list */
+ rn = nb_running_get_entry(dnode, NULL, true);
+ rdistance = rn->info;
+ if (rdistance->access_list)
+ free(rdistance->access_list);
+ rdistance->access_list = strdup(acl_name);
+
+ return NB_OK;
+}
+
+int ripd_instance_distance_source_access_list_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ struct route_node *rn;
+ struct rip_distance *rdistance;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ /* Reset access-list configuration. */
+ rn = nb_running_get_entry(dnode, NULL, true);
+ rdistance = rn->info;
+ free(rdistance->access_list);
+ rdistance->access_list = NULL;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/explicit-neighbor
+ */
+int ripd_instance_explicit_neighbor_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+ struct prefix_ipv4 p;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_BITLEN;
+ yang_dnode_get_ipv4(&p.prefix, dnode, NULL);
+
+ return rip_neighbor_add(rip, &p);
+}
+
+int ripd_instance_explicit_neighbor_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct rip *rip;
+ struct prefix_ipv4 p;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_BITLEN;
+ yang_dnode_get_ipv4(&p.prefix, dnode, NULL);
+
+ return rip_neighbor_delete(rip, &p);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/network
+ */
+int ripd_instance_network_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+ struct prefix p;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ yang_dnode_get_ipv4p(&p, dnode, NULL);
+ apply_mask_ipv4((struct prefix_ipv4 *)&p);
+
+ return rip_enable_network_add(rip, &p);
+}
+
+int ripd_instance_network_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct rip *rip;
+ struct prefix p;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ yang_dnode_get_ipv4p(&p, dnode, NULL);
+ apply_mask_ipv4((struct prefix_ipv4 *)&p);
+
+ return rip_enable_network_delete(rip, &p);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/interface
+ */
+int ripd_instance_interface_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+ const char *ifname;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ ifname = yang_dnode_get_string(dnode, NULL);
+
+ return rip_enable_if_add(rip, ifname);
+}
+
+int ripd_instance_interface_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct rip *rip;
+ const char *ifname;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ ifname = yang_dnode_get_string(dnode, NULL);
+
+ return rip_enable_if_delete(rip, ifname);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/offset-list
+ */
+int ripd_instance_offset_list_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+ const char *ifname;
+ struct rip_offset_list *offset;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ ifname = yang_dnode_get_string(dnode, "./interface");
+
+ offset = rip_offset_list_new(rip, ifname);
+ nb_running_set_entry(dnode, offset);
+
+ return NB_OK;
+}
+
+int ripd_instance_offset_list_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ int direct;
+ struct rip_offset_list *offset;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ direct = yang_dnode_get_enum(dnode, "./direction");
+
+ offset = nb_running_unset_entry(dnode);
+ if (offset->direct[direct].alist_name) {
+ free(offset->direct[direct].alist_name);
+ offset->direct[direct].alist_name = NULL;
+ }
+ if (offset->direct[RIP_OFFSET_LIST_IN].alist_name == NULL
+ && offset->direct[RIP_OFFSET_LIST_OUT].alist_name == NULL)
+ offset_list_del(offset);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/offset-list/access-list
+ */
+int ripd_instance_offset_list_access_list_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ int direct;
+ struct rip_offset_list *offset;
+ const char *alist_name;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ direct = yang_dnode_get_enum(dnode, "../direction");
+ alist_name = yang_dnode_get_string(dnode, NULL);
+
+ offset = nb_running_get_entry(dnode, NULL, true);
+ if (offset->direct[direct].alist_name)
+ free(offset->direct[direct].alist_name);
+ offset->direct[direct].alist_name = strdup(alist_name);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/offset-list/metric
+ */
+int ripd_instance_offset_list_metric_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ int direct;
+ uint8_t metric;
+ struct rip_offset_list *offset;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ direct = yang_dnode_get_enum(dnode, "../direction");
+ metric = yang_dnode_get_uint8(dnode, NULL);
+
+ offset = nb_running_get_entry(dnode, NULL, true);
+ offset->direct[direct].metric = metric;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/passive-default
+ */
+int ripd_instance_passive_default_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ rip->passive_default = yang_dnode_get_bool(dnode, NULL);
+ rip_passive_nondefault_clean(rip);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/passive-interface
+ */
+int ripd_instance_passive_interface_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+ const char *ifname;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ ifname = yang_dnode_get_string(dnode, NULL);
+
+ return rip_passive_nondefault_set(rip, ifname);
+}
+
+int ripd_instance_passive_interface_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct rip *rip;
+ const char *ifname;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ ifname = yang_dnode_get_string(dnode, NULL);
+
+ return rip_passive_nondefault_unset(rip, ifname);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/non-passive-interface
+ */
+int ripd_instance_non_passive_interface_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+ const char *ifname;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ ifname = yang_dnode_get_string(dnode, NULL);
+
+ return rip_passive_nondefault_unset(rip, ifname);
+}
+
+int ripd_instance_non_passive_interface_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct rip *rip;
+ const char *ifname;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ ifname = yang_dnode_get_string(dnode, NULL);
+
+ return rip_passive_nondefault_set(rip, ifname);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/redistribute
+ */
+int ripd_instance_redistribute_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+ int type;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ type = yang_dnode_get_enum(dnode, "./protocol");
+
+ rip->redist[type].enabled = true;
+
+ return NB_OK;
+}
+
+int ripd_instance_redistribute_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct rip *rip;
+ int type;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ type = yang_dnode_get_enum(dnode, "./protocol");
+
+ rip->redist[type].enabled = false;
+ if (rip->redist[type].route_map.name) {
+ free(rip->redist[type].route_map.name);
+ rip->redist[type].route_map.name = NULL;
+ rip->redist[type].route_map.map = NULL;
+ }
+ rip->redist[type].metric_config = false;
+ rip->redist[type].metric = 0;
+
+ if (rip->enabled)
+ rip_redistribute_conf_delete(rip, type);
+
+ return NB_OK;
+}
+
+void ripd_instance_redistribute_apply_finish(const struct lyd_node *dnode)
+{
+ struct rip *rip;
+ int type;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ type = yang_dnode_get_enum(dnode, "./protocol");
+
+ if (rip->enabled)
+ rip_redistribute_conf_update(rip, type);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/redistribute/route-map
+ */
+int ripd_instance_redistribute_route_map_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+ int type;
+ const char *rmap_name;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ type = yang_dnode_get_enum(dnode, "../protocol");
+ rmap_name = yang_dnode_get_string(dnode, NULL);
+
+ if (rip->redist[type].route_map.name)
+ free(rip->redist[type].route_map.name);
+ rip->redist[type].route_map.name = strdup(rmap_name);
+ rip->redist[type].route_map.map = route_map_lookup_by_name(rmap_name);
+
+ return NB_OK;
+}
+
+int ripd_instance_redistribute_route_map_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct rip *rip;
+ int type;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ type = yang_dnode_get_enum(dnode, "../protocol");
+
+ free(rip->redist[type].route_map.name);
+ rip->redist[type].route_map.name = NULL;
+ rip->redist[type].route_map.map = NULL;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/redistribute/metric
+ */
+int ripd_instance_redistribute_metric_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+ int type;
+ uint8_t metric;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ type = yang_dnode_get_enum(dnode, "../protocol");
+ metric = yang_dnode_get_uint8(dnode, NULL);
+
+ rip->redist[type].metric_config = true;
+ rip->redist[type].metric = metric;
+
+ return NB_OK;
+}
+
+int ripd_instance_redistribute_metric_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct rip *rip;
+ int type;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ type = yang_dnode_get_enum(dnode, "../protocol");
+
+ rip->redist[type].metric_config = false;
+ rip->redist[type].metric = 0;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/static-route
+ */
+int ripd_instance_static_route_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+ struct nexthop nh;
+ struct prefix_ipv4 p;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ yang_dnode_get_ipv4p(&p, dnode, NULL);
+ apply_mask_ipv4(&p);
+
+ memset(&nh, 0, sizeof(nh));
+ nh.type = NEXTHOP_TYPE_IPV4;
+ rip_redistribute_add(rip, ZEBRA_ROUTE_RIP, RIP_ROUTE_STATIC, &p, &nh, 0,
+ 0, 0);
+
+ return NB_OK;
+}
+
+int ripd_instance_static_route_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct rip *rip;
+ struct prefix_ipv4 p;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ yang_dnode_get_ipv4p(&p, dnode, NULL);
+ apply_mask_ipv4(&p);
+
+ rip_redistribute_delete(rip, ZEBRA_ROUTE_RIP, RIP_ROUTE_STATIC, &p, 0);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/timers/
+ */
+void ripd_instance_timers_apply_finish(const struct lyd_node *dnode)
+{
+ struct rip *rip;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+
+ /* Reset update timer thread. */
+ rip_event(rip, RIP_UPDATE_EVENT, 0);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/timers/flush-interval
+ */
+int ripd_instance_timers_flush_interval_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ rip->garbage_time = yang_dnode_get_uint32(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/timers/holddown-interval
+ */
+int ripd_instance_timers_holddown_interval_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ rip->timeout_time = yang_dnode_get_uint32(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/timers/update-interval
+ */
+int ripd_instance_timers_update_interval_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ rip->update_time = yang_dnode_get_uint32(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/version/receive
+ */
+int ripd_instance_version_receive_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ rip->version_recv = yang_dnode_get_enum(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/version/send
+ */
+int ripd_instance_version_send_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct rip *rip;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(dnode, NULL, true);
+ rip->version_send = yang_dnode_get_enum(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/split-horizon
+ */
+int lib_interface_rip_split_horizon_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(dnode, NULL, true);
+ ri = ifp->info;
+ ri->split_horizon = yang_dnode_get_enum(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/v2-broadcast
+ */
+int lib_interface_rip_v2_broadcast_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(dnode, NULL, true);
+ ri = ifp->info;
+ ri->v2_broadcast = yang_dnode_get_bool(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/version-receive
+ */
+int lib_interface_rip_version_receive_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(dnode, NULL, true);
+ ri = ifp->info;
+ ri->ri_receive = yang_dnode_get_enum(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/version-send
+ */
+int lib_interface_rip_version_send_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(dnode, NULL, true);
+ ri = ifp->info;
+ ri->ri_send = yang_dnode_get_enum(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-scheme/mode
+ */
+int lib_interface_rip_authentication_scheme_mode_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(dnode, NULL, true);
+ ri = ifp->info;
+ ri->auth_type = yang_dnode_get_enum(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-ripd:rip/authentication-scheme/md5-auth-length
+ */
+int lib_interface_rip_authentication_scheme_md5_auth_length_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(dnode, NULL, true);
+ ri = ifp->info;
+ ri->md5_auth_len = yang_dnode_get_enum(dnode, NULL);
+
+ return NB_OK;
+}
+
+int lib_interface_rip_authentication_scheme_md5_auth_length_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(dnode, NULL, true);
+ ri = ifp->info;
+ ri->md5_auth_len = yang_get_default_enum(
+ "%s/authentication-scheme/md5-auth-length", RIP_IFACE);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-password
+ */
+int lib_interface_rip_authentication_password_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(dnode, NULL, true);
+ ri = ifp->info;
+ XFREE(MTYPE_RIP_INTERFACE_STRING, ri->auth_str);
+ ri->auth_str = XSTRDUP(MTYPE_RIP_INTERFACE_STRING,
+ yang_dnode_get_string(dnode, NULL));
+
+ return NB_OK;
+}
+
+int lib_interface_rip_authentication_password_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(dnode, NULL, true);
+ ri = ifp->info;
+ XFREE(MTYPE_RIP_INTERFACE_STRING, ri->auth_str);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-key-chain
+ */
+int lib_interface_rip_authentication_key_chain_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(dnode, NULL, true);
+ ri = ifp->info;
+ XFREE(MTYPE_RIP_INTERFACE_STRING, ri->key_chain);
+ ri->key_chain = XSTRDUP(MTYPE_RIP_INTERFACE_STRING,
+ yang_dnode_get_string(dnode, NULL));
+
+ return NB_OK;
+}
+
+int lib_interface_rip_authentication_key_chain_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(dnode, NULL, true);
+ ri = ifp->info;
+ XFREE(MTYPE_RIP_INTERFACE_STRING, ri->key_chain);
+
+ return NB_OK;
+}
diff --git a/ripd/rip_nb_notifications.c b/ripd/rip_nb_notifications.c
new file mode 100644
index 0000000000..28d3517dfd
--- /dev/null
+++ b/ripd/rip_nb_notifications.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "vrf.h"
+#include "log.h"
+#include "prefix.h"
+#include "table.h"
+#include "command.h"
+#include "routemap.h"
+#include "northbound.h"
+#include "libfrr.h"
+
+#include "ripd/ripd.h"
+#include "ripd/rip_nb.h"
+#include "ripd/rip_debug.h"
+#include "ripd/rip_interface.h"
+
+/*
+ * XPath: /frr-ripd:authentication-type-failure
+ */
+void ripd_notif_send_auth_type_failure(const char *ifname)
+{
+ const char *xpath = "/frr-ripd:authentication-type-failure";
+ struct list *arguments;
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+
+ arguments = yang_data_list_new();
+
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/interface-name", xpath);
+ data = yang_data_new_string(xpath_arg, ifname);
+ listnode_add(arguments, data);
+
+ nb_notification_send(xpath, arguments);
+}
+
+/*
+ * XPath: /frr-ripd:authentication-failure
+ */
+void ripd_notif_send_auth_failure(const char *ifname)
+{
+ const char *xpath = "/frr-ripd:authentication-failure";
+ struct list *arguments;
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+
+ arguments = yang_data_list_new();
+
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/interface-name", xpath);
+ data = yang_data_new_string(xpath_arg, ifname);
+ listnode_add(arguments, data);
+
+ nb_notification_send(xpath, arguments);
+}
diff --git a/ripd/rip_nb_rpcs.c b/ripd/rip_nb_rpcs.c
new file mode 100644
index 0000000000..0ca85296fd
--- /dev/null
+++ b/ripd/rip_nb_rpcs.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "vrf.h"
+#include "log.h"
+#include "prefix.h"
+#include "table.h"
+#include "command.h"
+#include "routemap.h"
+#include "northbound.h"
+#include "libfrr.h"
+
+#include "ripd/ripd.h"
+#include "ripd/rip_nb.h"
+#include "ripd/rip_debug.h"
+#include "ripd/rip_interface.h"
+
+/*
+ * XPath: /frr-ripd:clear-rip-route
+ */
+static void clear_rip_route(struct rip *rip)
+{
+ struct route_node *rp;
+
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("Clearing all RIP routes (VRF %s)", rip->vrf_name);
+
+ /* Clear received RIP routes */
+ for (rp = route_top(rip->table); rp; rp = route_next(rp)) {
+ struct list *list;
+ struct listnode *listnode;
+ struct rip_info *rinfo;
+
+ list = rp->info;
+ if (!list)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) {
+ if (!rip_route_rte(rinfo))
+ continue;
+
+ if (CHECK_FLAG(rinfo->flags, RIP_RTF_FIB))
+ rip_zebra_ipv4_delete(rip, rp);
+ break;
+ }
+
+ if (rinfo) {
+ RIP_TIMER_OFF(rinfo->t_timeout);
+ RIP_TIMER_OFF(rinfo->t_garbage_collect);
+ listnode_delete(list, rinfo);
+ rip_info_free(rinfo);
+ }
+
+ if (list_isempty(list)) {
+ list_delete(&list);
+ rp->info = NULL;
+ route_unlock_node(rp);
+ }
+ }
+}
+
+int clear_rip_route_rpc(const char *xpath, const struct list *input,
+ struct list *output)
+{
+ struct rip *rip;
+ struct yang_data *yang_vrf;
+
+ yang_vrf = yang_data_list_find(input, "%s/%s", xpath, "input/vrf");
+ if (yang_vrf) {
+ rip = rip_lookup_by_vrf_name(yang_vrf->value);
+ if (rip)
+ clear_rip_route(rip);
+ } else {
+ struct vrf *vrf;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ rip = vrf->info;
+ if (!rip)
+ continue;
+
+ clear_rip_route(rip);
+ }
+ }
+
+ return NB_OK;
+}
diff --git a/ripd/rip_nb_state.c b/ripd/rip_nb_state.c
new file mode 100644
index 0000000000..2f7eb3cb5e
--- /dev/null
+++ b/ripd/rip_nb_state.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "vrf.h"
+#include "log.h"
+#include "prefix.h"
+#include "table.h"
+#include "command.h"
+#include "routemap.h"
+#include "northbound.h"
+#include "libfrr.h"
+
+#include "ripd/ripd.h"
+#include "ripd/rip_nb.h"
+#include "ripd/rip_debug.h"
+#include "ripd/rip_interface.h"
+
+/*
+ * XPath: /frr-ripd:ripd/instance
+ */
+const void *ripd_instance_get_next(const void *parent_list_entry,
+ const void *list_entry)
+{
+ struct rip *rip = (struct rip *)list_entry;
+
+ if (list_entry == NULL)
+ rip = RB_MIN(rip_instance_head, &rip_instances);
+ else
+ rip = RB_NEXT(rip_instance_head, rip);
+
+ return rip;
+}
+
+int ripd_instance_get_keys(const void *list_entry, struct yang_list_keys *keys)
+{
+ const struct rip *rip = list_entry;
+
+ keys->num = 1;
+ strlcpy(keys->key[0], rip->vrf_name, sizeof(keys->key[0]));
+
+ return NB_OK;
+}
+
+const void *ripd_instance_lookup_entry(const void *parent_list_entry,
+ const struct yang_list_keys *keys)
+{
+ const char *vrf_name = keys->key[0];
+
+ return rip_lookup_by_vrf_name(vrf_name);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/neighbors/neighbor
+ */
+const void *
+ripd_instance_state_neighbors_neighbor_get_next(const void *parent_list_entry,
+ const void *list_entry)
+{
+ const struct rip *rip = parent_list_entry;
+ struct listnode *node;
+
+ if (list_entry == NULL)
+ node = listhead(rip->peer_list);
+ else
+ node = listnextnode((struct listnode *)list_entry);
+
+ return node;
+}
+
+int ripd_instance_state_neighbors_neighbor_get_keys(const void *list_entry,
+ struct yang_list_keys *keys)
+{
+ const struct listnode *node = list_entry;
+ const struct rip_peer *peer = listgetdata(node);
+
+ keys->num = 1;
+ (void)inet_ntop(AF_INET, &peer->addr, keys->key[0],
+ sizeof(keys->key[0]));
+
+ return NB_OK;
+}
+
+const void *ripd_instance_state_neighbors_neighbor_lookup_entry(
+ const void *parent_list_entry, const struct yang_list_keys *keys)
+{
+ const struct rip *rip = parent_list_entry;
+ struct in_addr address;
+ struct rip_peer *peer;
+ struct listnode *node;
+
+ yang_str2ipv4(keys->key[0], &address);
+
+ for (ALL_LIST_ELEMENTS_RO(rip->peer_list, node, peer)) {
+ if (IPV4_ADDR_SAME(&peer->addr, &address))
+ return node;
+ }
+
+ return NULL;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/neighbors/neighbor/address
+ */
+struct yang_data *
+ripd_instance_state_neighbors_neighbor_address_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct listnode *node = list_entry;
+ const struct rip_peer *peer = listgetdata(node);
+
+ return yang_data_new_ipv4(xpath, &peer->addr);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/neighbors/neighbor/last-update
+ */
+struct yang_data *ripd_instance_state_neighbors_neighbor_last_update_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ /* TODO: yang:date-and-time is tricky */
+ return NULL;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/neighbors/neighbor/bad-packets-rcvd
+ */
+struct yang_data *
+ripd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct listnode *node = list_entry;
+ const struct rip_peer *peer = listgetdata(node);
+
+ return yang_data_new_uint32(xpath, peer->recv_badpackets);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/neighbors/neighbor/bad-routes-rcvd
+ */
+struct yang_data *
+ripd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct listnode *node = list_entry;
+ const struct rip_peer *peer = listgetdata(node);
+
+ return yang_data_new_uint32(xpath, peer->recv_badroutes);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route
+ */
+const void *
+ripd_instance_state_routes_route_get_next(const void *parent_list_entry,
+ const void *list_entry)
+{
+ const struct rip *rip = parent_list_entry;
+ struct route_node *rn;
+
+ if (list_entry == NULL)
+ rn = route_top(rip->table);
+ else
+ rn = route_next((struct route_node *)list_entry);
+ while (rn && rn->info == NULL)
+ rn = route_next(rn);
+
+ return rn;
+}
+
+int ripd_instance_state_routes_route_get_keys(const void *list_entry,
+ struct yang_list_keys *keys)
+{
+ const struct route_node *rn = list_entry;
+
+ keys->num = 1;
+ (void)prefix2str(&rn->p, keys->key[0], sizeof(keys->key[0]));
+
+ return NB_OK;
+}
+
+const void *
+ripd_instance_state_routes_route_lookup_entry(const void *parent_list_entry,
+ const struct yang_list_keys *keys)
+{
+ const struct rip *rip = parent_list_entry;
+ struct prefix prefix;
+ struct route_node *rn;
+
+ yang_str2ipv4p(keys->key[0], &prefix);
+
+ rn = route_node_lookup(rip->table, &prefix);
+ if (!rn || !rn->info)
+ return NULL;
+
+ route_unlock_node(rn);
+
+ return rn;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/prefix
+ */
+struct yang_data *
+ripd_instance_state_routes_route_prefix_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct route_node *rn = list_entry;
+ const struct rip_info *rinfo = listnode_head(rn->info);
+
+ return yang_data_new_ipv4p(xpath, &rinfo->rp->p);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/next-hop
+ */
+struct yang_data *
+ripd_instance_state_routes_route_next_hop_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct route_node *rn = list_entry;
+ const struct rip_info *rinfo = listnode_head(rn->info);
+
+ switch (rinfo->nh.type) {
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ return yang_data_new_ipv4(xpath, &rinfo->nh.gate.ipv4);
+ default:
+ return NULL;
+ }
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/interface
+ */
+struct yang_data *
+ripd_instance_state_routes_route_interface_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct route_node *rn = list_entry;
+ const struct rip_info *rinfo = listnode_head(rn->info);
+ const struct rip *rip = rip_info_get_instance(rinfo);
+
+ switch (rinfo->nh.type) {
+ case NEXTHOP_TYPE_IFINDEX:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ return yang_data_new_string(
+ xpath,
+ ifindex2ifname(rinfo->nh.ifindex, rip->vrf->vrf_id));
+ default:
+ return NULL;
+ }
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/metric
+ */
+struct yang_data *
+ripd_instance_state_routes_route_metric_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct route_node *rn = list_entry;
+ const struct rip_info *rinfo = listnode_head(rn->info);
+
+ return yang_data_new_uint8(xpath, rinfo->metric);
+}
diff --git a/ripd/rip_northbound.c b/ripd/rip_northbound.c
deleted file mode 100644
index 1238d0f0fc..0000000000
--- a/ripd/rip_northbound.c
+++ /dev/null
@@ -1,1823 +0,0 @@
-/*
- * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
- * Copyright (C) 2018 NetDEF, Inc.
- * Renato Westphal
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; see the file COPYING; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <zebra.h>
-
-#include "if.h"
-#include "vrf.h"
-#include "log.h"
-#include "prefix.h"
-#include "table.h"
-#include "command.h"
-#include "routemap.h"
-#include "northbound.h"
-#include "libfrr.h"
-
-#include "ripd/ripd.h"
-#include "ripd/rip_debug.h"
-#include "ripd/rip_cli.h"
-#include "ripd/rip_interface.h"
-
-/*
- * XPath: /frr-ripd:ripd/instance
- */
-static int ripd_instance_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
- struct vrf *vrf;
- const char *vrf_name;
- int socket;
-
- vrf_name = yang_dnode_get_string(dnode, "./vrf");
- vrf = vrf_lookup_by_name(vrf_name);
-
- /*
- * Try to create a RIP socket only if the VRF is enabled, otherwise
- * create a disabled RIP instance and wait for the VRF to be enabled.
- */
- switch (event) {
- case NB_EV_VALIDATE:
- break;
- case NB_EV_PREPARE:
- if (!vrf || !vrf_is_enabled(vrf))
- break;
-
- socket = rip_create_socket(vrf);
- if (socket < 0)
- return NB_ERR_RESOURCE;
- resource->fd = socket;
- break;
- case NB_EV_ABORT:
- if (!vrf || !vrf_is_enabled(vrf))
- break;
-
- socket = resource->fd;
- close(socket);
- break;
- case NB_EV_APPLY:
- if (vrf && vrf_is_enabled(vrf))
- socket = resource->fd;
- else
- socket = -1;
-
- rip = rip_create(vrf_name, vrf, socket);
- nb_running_set_entry(dnode, rip);
- break;
- }
-
- return NB_OK;
-}
-
-static int ripd_instance_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct rip *rip;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_unset_entry(dnode);
- rip_clean(rip);
-
- return NB_OK;
-}
-
-static const void *ripd_instance_get_next(const void *parent_list_entry,
- const void *list_entry)
-{
- struct rip *rip = (struct rip *)list_entry;
-
- if (list_entry == NULL)
- rip = RB_MIN(rip_instance_head, &rip_instances);
- else
- rip = RB_NEXT(rip_instance_head, rip);
-
- return rip;
-}
-
-static int ripd_instance_get_keys(const void *list_entry,
- struct yang_list_keys *keys)
-{
- const struct rip *rip = list_entry;
-
- keys->num = 1;
- strlcpy(keys->key[0], rip->vrf_name, sizeof(keys->key[0]));
-
- return NB_OK;
-}
-
-static const void *ripd_instance_lookup_entry(const void *parent_list_entry,
- const struct yang_list_keys *keys)
-{
- const char *vrf_name = keys->key[0];
-
- return rip_lookup_by_vrf_name(vrf_name);
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/allow-ecmp
- */
-static int ripd_instance_allow_ecmp_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- rip->ecmp = yang_dnode_get_bool(dnode, NULL);
- if (!rip->ecmp)
- rip_ecmp_disable(rip);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/default-information-originate
- */
-static int
-ripd_instance_default_information_originate_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
- bool default_information;
- struct prefix_ipv4 p;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- default_information = yang_dnode_get_bool(dnode, NULL);
-
- memset(&p, 0, sizeof(struct prefix_ipv4));
- p.family = AF_INET;
- if (default_information) {
- struct nexthop nh;
-
- memset(&nh, 0, sizeof(nh));
- nh.type = NEXTHOP_TYPE_IPV4;
- rip_redistribute_add(rip, ZEBRA_ROUTE_RIP, RIP_ROUTE_DEFAULT,
- &p, &nh, 0, 0, 0);
- } else {
- rip_redistribute_delete(rip, ZEBRA_ROUTE_RIP, RIP_ROUTE_DEFAULT,
- &p, 0);
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/default-metric
- */
-static int ripd_instance_default_metric_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- rip->default_metric = yang_dnode_get_uint8(dnode, NULL);
- /* rip_update_default_metric (); */
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/distance/default
- */
-static int ripd_instance_distance_default_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- rip->distance = yang_dnode_get_uint8(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/distance/source
- */
-static int ripd_instance_distance_source_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
- struct prefix_ipv4 prefix;
- struct route_node *rn;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- yang_dnode_get_ipv4p(&prefix, dnode, "./prefix");
- apply_mask_ipv4(&prefix);
-
- /* Get RIP distance node. */
- rip = nb_running_get_entry(dnode, NULL, true);
- rn = route_node_get(rip->distance_table, (struct prefix *)&prefix);
- rn->info = rip_distance_new();
- nb_running_set_entry(dnode, rn);
-
- return NB_OK;
-}
-
-static int ripd_instance_distance_source_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct route_node *rn;
- struct rip_distance *rdistance;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rn = nb_running_unset_entry(dnode);
- rdistance = rn->info;
- rip_distance_free(rdistance);
- rn->info = NULL;
- route_unlock_node(rn);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/distance/source/distance
- */
-static int
-ripd_instance_distance_source_distance_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct route_node *rn;
- uint8_t distance;
- struct rip_distance *rdistance;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- /* Set distance value. */
- rn = nb_running_get_entry(dnode, NULL, true);
- distance = yang_dnode_get_uint8(dnode, NULL);
- rdistance = rn->info;
- rdistance->distance = distance;
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/distance/source/access-list
- */
-static int
-ripd_instance_distance_source_access_list_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- const char *acl_name;
- struct route_node *rn;
- struct rip_distance *rdistance;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- acl_name = yang_dnode_get_string(dnode, NULL);
-
- /* Set access-list */
- rn = nb_running_get_entry(dnode, NULL, true);
- rdistance = rn->info;
- if (rdistance->access_list)
- free(rdistance->access_list);
- rdistance->access_list = strdup(acl_name);
-
- return NB_OK;
-}
-
-static int
-ripd_instance_distance_source_access_list_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct route_node *rn;
- struct rip_distance *rdistance;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- /* Reset access-list configuration. */
- rn = nb_running_get_entry(dnode, NULL, true);
- rdistance = rn->info;
- free(rdistance->access_list);
- rdistance->access_list = NULL;
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/explicit-neighbor
- */
-static int ripd_instance_explicit_neighbor_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
- struct prefix_ipv4 p;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- p.family = AF_INET;
- p.prefixlen = IPV4_MAX_BITLEN;
- yang_dnode_get_ipv4(&p.prefix, dnode, NULL);
-
- return rip_neighbor_add(rip, &p);
-}
-
-static int ripd_instance_explicit_neighbor_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct rip *rip;
- struct prefix_ipv4 p;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- p.family = AF_INET;
- p.prefixlen = IPV4_MAX_BITLEN;
- yang_dnode_get_ipv4(&p.prefix, dnode, NULL);
-
- return rip_neighbor_delete(rip, &p);
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/network
- */
-static int ripd_instance_network_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
- struct prefix p;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- yang_dnode_get_ipv4p(&p, dnode, NULL);
- apply_mask_ipv4((struct prefix_ipv4 *)&p);
-
- return rip_enable_network_add(rip, &p);
-}
-
-static int ripd_instance_network_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct rip *rip;
- struct prefix p;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- yang_dnode_get_ipv4p(&p, dnode, NULL);
- apply_mask_ipv4((struct prefix_ipv4 *)&p);
-
- return rip_enable_network_delete(rip, &p);
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/interface
- */
-static int ripd_instance_interface_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
- const char *ifname;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- ifname = yang_dnode_get_string(dnode, NULL);
-
- return rip_enable_if_add(rip, ifname);
-}
-
-static int ripd_instance_interface_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct rip *rip;
- const char *ifname;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- ifname = yang_dnode_get_string(dnode, NULL);
-
- return rip_enable_if_delete(rip, ifname);
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/offset-list
- */
-static int ripd_instance_offset_list_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
- const char *ifname;
- struct rip_offset_list *offset;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- ifname = yang_dnode_get_string(dnode, "./interface");
-
- offset = rip_offset_list_new(rip, ifname);
- nb_running_set_entry(dnode, offset);
-
- return NB_OK;
-}
-
-static int ripd_instance_offset_list_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- int direct;
- struct rip_offset_list *offset;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- direct = yang_dnode_get_enum(dnode, "./direction");
-
- offset = nb_running_unset_entry(dnode);
- if (offset->direct[direct].alist_name) {
- free(offset->direct[direct].alist_name);
- offset->direct[direct].alist_name = NULL;
- }
- if (offset->direct[RIP_OFFSET_LIST_IN].alist_name == NULL
- && offset->direct[RIP_OFFSET_LIST_OUT].alist_name == NULL)
- offset_list_del(offset);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/offset-list/access-list
- */
-static int
-ripd_instance_offset_list_access_list_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- int direct;
- struct rip_offset_list *offset;
- const char *alist_name;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- direct = yang_dnode_get_enum(dnode, "../direction");
- alist_name = yang_dnode_get_string(dnode, NULL);
-
- offset = nb_running_get_entry(dnode, NULL, true);
- if (offset->direct[direct].alist_name)
- free(offset->direct[direct].alist_name);
- offset->direct[direct].alist_name = strdup(alist_name);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/offset-list/metric
- */
-static int ripd_instance_offset_list_metric_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- int direct;
- uint8_t metric;
- struct rip_offset_list *offset;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- direct = yang_dnode_get_enum(dnode, "../direction");
- metric = yang_dnode_get_uint8(dnode, NULL);
-
- offset = nb_running_get_entry(dnode, NULL, true);
- offset->direct[direct].metric = metric;
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/passive-default
- */
-static int ripd_instance_passive_default_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- rip->passive_default = yang_dnode_get_bool(dnode, NULL);
- rip_passive_nondefault_clean(rip);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/passive-interface
- */
-static int ripd_instance_passive_interface_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
- const char *ifname;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- ifname = yang_dnode_get_string(dnode, NULL);
-
- return rip_passive_nondefault_set(rip, ifname);
-}
-
-static int ripd_instance_passive_interface_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct rip *rip;
- const char *ifname;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- ifname = yang_dnode_get_string(dnode, NULL);
-
- return rip_passive_nondefault_unset(rip, ifname);
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/non-passive-interface
- */
-static int
-ripd_instance_non_passive_interface_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
- const char *ifname;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- ifname = yang_dnode_get_string(dnode, NULL);
-
- return rip_passive_nondefault_unset(rip, ifname);
-}
-
-static int
-ripd_instance_non_passive_interface_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct rip *rip;
- const char *ifname;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- ifname = yang_dnode_get_string(dnode, NULL);
-
- return rip_passive_nondefault_set(rip, ifname);
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/redistribute
- */
-static int ripd_instance_redistribute_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
- int type;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- type = yang_dnode_get_enum(dnode, "./protocol");
-
- rip->redist[type].enabled = true;
-
- return NB_OK;
-}
-
-static int ripd_instance_redistribute_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct rip *rip;
- int type;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- type = yang_dnode_get_enum(dnode, "./protocol");
-
- rip->redist[type].enabled = false;
- if (rip->redist[type].route_map.name) {
- free(rip->redist[type].route_map.name);
- rip->redist[type].route_map.name = NULL;
- rip->redist[type].route_map.map = NULL;
- }
- rip->redist[type].metric_config = false;
- rip->redist[type].metric = 0;
-
- if (rip->enabled)
- rip_redistribute_conf_delete(rip, type);
-
- return NB_OK;
-}
-
-static void
-ripd_instance_redistribute_apply_finish(const struct lyd_node *dnode)
-{
- struct rip *rip;
- int type;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- type = yang_dnode_get_enum(dnode, "./protocol");
-
- if (rip->enabled)
- rip_redistribute_conf_update(rip, type);
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/redistribute/route-map
- */
-static int
-ripd_instance_redistribute_route_map_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
- int type;
- const char *rmap_name;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- type = yang_dnode_get_enum(dnode, "../protocol");
- rmap_name = yang_dnode_get_string(dnode, NULL);
-
- if (rip->redist[type].route_map.name)
- free(rip->redist[type].route_map.name);
- rip->redist[type].route_map.name = strdup(rmap_name);
- rip->redist[type].route_map.map = route_map_lookup_by_name(rmap_name);
-
- return NB_OK;
-}
-
-static int
-ripd_instance_redistribute_route_map_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct rip *rip;
- int type;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- type = yang_dnode_get_enum(dnode, "../protocol");
-
- free(rip->redist[type].route_map.name);
- rip->redist[type].route_map.name = NULL;
- rip->redist[type].route_map.map = NULL;
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/redistribute/metric
- */
-static int
-ripd_instance_redistribute_metric_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
- int type;
- uint8_t metric;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- type = yang_dnode_get_enum(dnode, "../protocol");
- metric = yang_dnode_get_uint8(dnode, NULL);
-
- rip->redist[type].metric_config = true;
- rip->redist[type].metric = metric;
-
- return NB_OK;
-}
-
-static int
-ripd_instance_redistribute_metric_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct rip *rip;
- int type;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- type = yang_dnode_get_enum(dnode, "../protocol");
-
- rip->redist[type].metric_config = false;
- rip->redist[type].metric = 0;
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/static-route
- */
-static int ripd_instance_static_route_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
- struct nexthop nh;
- struct prefix_ipv4 p;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- yang_dnode_get_ipv4p(&p, dnode, NULL);
- apply_mask_ipv4(&p);
-
- memset(&nh, 0, sizeof(nh));
- nh.type = NEXTHOP_TYPE_IPV4;
- rip_redistribute_add(rip, ZEBRA_ROUTE_RIP, RIP_ROUTE_STATIC, &p, &nh, 0,
- 0, 0);
-
- return NB_OK;
-}
-
-static int ripd_instance_static_route_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct rip *rip;
- struct prefix_ipv4 p;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- yang_dnode_get_ipv4p(&p, dnode, NULL);
- apply_mask_ipv4(&p);
-
- rip_redistribute_delete(rip, ZEBRA_ROUTE_RIP, RIP_ROUTE_STATIC, &p, 0);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/timers/
- */
-static void ripd_instance_timers_apply_finish(const struct lyd_node *dnode)
-{
- struct rip *rip;
-
- rip = nb_running_get_entry(dnode, NULL, true);
-
- /* Reset update timer thread. */
- rip_event(rip, RIP_UPDATE_EVENT, 0);
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/timers/flush-interval
- */
-static int
-ripd_instance_timers_flush_interval_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- rip->garbage_time = yang_dnode_get_uint32(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/timers/holddown-interval
- */
-static int
-ripd_instance_timers_holddown_interval_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- rip->timeout_time = yang_dnode_get_uint32(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/timers/update-interval
- */
-static int
-ripd_instance_timers_update_interval_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- rip->update_time = yang_dnode_get_uint32(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/version/receive
- */
-static int ripd_instance_version_receive_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- rip->version_recv = yang_dnode_get_enum(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/version/send
- */
-static int ripd_instance_version_send_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct rip *rip;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- rip = nb_running_get_entry(dnode, NULL, true);
- rip->version_send = yang_dnode_get_enum(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-ripd:rip/split-horizon
- */
-static int lib_interface_rip_split_horizon_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct interface *ifp;
- struct rip_interface *ri;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ifp = nb_running_get_entry(dnode, NULL, true);
- ri = ifp->info;
- ri->split_horizon = yang_dnode_get_enum(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-ripd:rip/v2-broadcast
- */
-static int lib_interface_rip_v2_broadcast_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct interface *ifp;
- struct rip_interface *ri;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ifp = nb_running_get_entry(dnode, NULL, true);
- ri = ifp->info;
- ri->v2_broadcast = yang_dnode_get_bool(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-ripd:rip/version-receive
- */
-static int
-lib_interface_rip_version_receive_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct interface *ifp;
- struct rip_interface *ri;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ifp = nb_running_get_entry(dnode, NULL, true);
- ri = ifp->info;
- ri->ri_receive = yang_dnode_get_enum(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-ripd:rip/version-send
- */
-static int lib_interface_rip_version_send_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct interface *ifp;
- struct rip_interface *ri;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ifp = nb_running_get_entry(dnode, NULL, true);
- ri = ifp->info;
- ri->ri_send = yang_dnode_get_enum(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-scheme/mode
- */
-static int lib_interface_rip_authentication_scheme_mode_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct interface *ifp;
- struct rip_interface *ri;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ifp = nb_running_get_entry(dnode, NULL, true);
- ri = ifp->info;
- ri->auth_type = yang_dnode_get_enum(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath:
- * /frr-interface:lib/interface/frr-ripd:rip/authentication-scheme/md5-auth-length
- */
-static int lib_interface_rip_authentication_scheme_md5_auth_length_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct interface *ifp;
- struct rip_interface *ri;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ifp = nb_running_get_entry(dnode, NULL, true);
- ri = ifp->info;
- ri->md5_auth_len = yang_dnode_get_enum(dnode, NULL);
-
- return NB_OK;
-}
-
-static int lib_interface_rip_authentication_scheme_md5_auth_length_destroy(
- enum nb_event event, const struct lyd_node *dnode)
-{
- struct interface *ifp;
- struct rip_interface *ri;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ifp = nb_running_get_entry(dnode, NULL, true);
- ri = ifp->info;
- ri->md5_auth_len = yang_get_default_enum(
- "%s/authentication-scheme/md5-auth-length", RIP_IFACE);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-password
- */
-static int
-lib_interface_rip_authentication_password_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct interface *ifp;
- struct rip_interface *ri;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ifp = nb_running_get_entry(dnode, NULL, true);
- ri = ifp->info;
- XFREE(MTYPE_RIP_INTERFACE_STRING, ri->auth_str);
- ri->auth_str = XSTRDUP(MTYPE_RIP_INTERFACE_STRING,
- yang_dnode_get_string(dnode, NULL));
-
- return NB_OK;
-}
-
-static int
-lib_interface_rip_authentication_password_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct interface *ifp;
- struct rip_interface *ri;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ifp = nb_running_get_entry(dnode, NULL, true);
- ri = ifp->info;
- XFREE(MTYPE_RIP_INTERFACE_STRING, ri->auth_str);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-key-chain
- */
-static int
-lib_interface_rip_authentication_key_chain_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct interface *ifp;
- struct rip_interface *ri;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ifp = nb_running_get_entry(dnode, NULL, true);
- ri = ifp->info;
- XFREE(MTYPE_RIP_INTERFACE_STRING, ri->key_chain);
- ri->key_chain = XSTRDUP(MTYPE_RIP_INTERFACE_STRING,
- yang_dnode_get_string(dnode, NULL));
-
- return NB_OK;
-}
-
-static int
-lib_interface_rip_authentication_key_chain_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct interface *ifp;
- struct rip_interface *ri;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ifp = nb_running_get_entry(dnode, NULL, true);
- ri = ifp->info;
- XFREE(MTYPE_RIP_INTERFACE_STRING, ri->key_chain);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/state/neighbors/neighbor
- */
-static const void *
-ripd_instance_state_neighbors_neighbor_get_next(const void *parent_list_entry,
- const void *list_entry)
-{
- const struct rip *rip = parent_list_entry;
- struct listnode *node;
-
- if (list_entry == NULL)
- node = listhead(rip->peer_list);
- else
- node = listnextnode((struct listnode *)list_entry);
-
- return node;
-}
-
-static int
-ripd_instance_state_neighbors_neighbor_get_keys(const void *list_entry,
- struct yang_list_keys *keys)
-{
- const struct listnode *node = list_entry;
- const struct rip_peer *peer = listgetdata(node);
-
- keys->num = 1;
- (void)inet_ntop(AF_INET, &peer->addr, keys->key[0],
- sizeof(keys->key[0]));
-
- return NB_OK;
-}
-
-static const void *ripd_instance_state_neighbors_neighbor_lookup_entry(
- const void *parent_list_entry, const struct yang_list_keys *keys)
-{
- const struct rip *rip = parent_list_entry;
- struct in_addr address;
- struct rip_peer *peer;
- struct listnode *node;
-
- yang_str2ipv4(keys->key[0], &address);
-
- for (ALL_LIST_ELEMENTS_RO(rip->peer_list, node, peer)) {
- if (IPV4_ADDR_SAME(&peer->addr, &address))
- return node;
- }
-
- return NULL;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/state/neighbors/neighbor/address
- */
-static struct yang_data *
-ripd_instance_state_neighbors_neighbor_address_get_elem(const char *xpath,
- const void *list_entry)
-{
- const struct listnode *node = list_entry;
- const struct rip_peer *peer = listgetdata(node);
-
- return yang_data_new_ipv4(xpath, &peer->addr);
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/state/neighbors/neighbor/last-update
- */
-static struct yang_data *
-ripd_instance_state_neighbors_neighbor_last_update_get_elem(
- const char *xpath, const void *list_entry)
-{
- /* TODO: yang:date-and-time is tricky */
- return NULL;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/state/neighbors/neighbor/bad-packets-rcvd
- */
-static struct yang_data *
-ripd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct listnode *node = list_entry;
- const struct rip_peer *peer = listgetdata(node);
-
- return yang_data_new_uint32(xpath, peer->recv_badpackets);
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/state/neighbors/neighbor/bad-routes-rcvd
- */
-static struct yang_data *
-ripd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct listnode *node = list_entry;
- const struct rip_peer *peer = listgetdata(node);
-
- return yang_data_new_uint32(xpath, peer->recv_badroutes);
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/state/routes/route
- */
-static const void *
-ripd_instance_state_routes_route_get_next(const void *parent_list_entry,
- const void *list_entry)
-{
- const struct rip *rip = parent_list_entry;
- struct route_node *rn;
-
- if (list_entry == NULL)
- rn = route_top(rip->table);
- else
- rn = route_next((struct route_node *)list_entry);
- while (rn && rn->info == NULL)
- rn = route_next(rn);
-
- return rn;
-}
-
-static int
-ripd_instance_state_routes_route_get_keys(const void *list_entry,
- struct yang_list_keys *keys)
-{
- const struct route_node *rn = list_entry;
-
- keys->num = 1;
- (void)prefix2str(&rn->p, keys->key[0], sizeof(keys->key[0]));
-
- return NB_OK;
-}
-
-static const void *
-ripd_instance_state_routes_route_lookup_entry(const void *parent_list_entry,
- const struct yang_list_keys *keys)
-{
- const struct rip *rip = parent_list_entry;
- struct prefix prefix;
- struct route_node *rn;
-
- yang_str2ipv4p(keys->key[0], &prefix);
-
- rn = route_node_lookup(rip->table, &prefix);
- if (!rn || !rn->info)
- return NULL;
-
- route_unlock_node(rn);
-
- return rn;
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/state/routes/route/prefix
- */
-static struct yang_data *
-ripd_instance_state_routes_route_prefix_get_elem(const char *xpath,
- const void *list_entry)
-{
- const struct route_node *rn = list_entry;
- const struct rip_info *rinfo = listnode_head(rn->info);
-
- return yang_data_new_ipv4p(xpath, &rinfo->rp->p);
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/state/routes/route/next-hop
- */
-static struct yang_data *
-ripd_instance_state_routes_route_next_hop_get_elem(const char *xpath,
- const void *list_entry)
-{
- const struct route_node *rn = list_entry;
- const struct rip_info *rinfo = listnode_head(rn->info);
-
- switch (rinfo->nh.type) {
- case NEXTHOP_TYPE_IPV4:
- case NEXTHOP_TYPE_IPV4_IFINDEX:
- return yang_data_new_ipv4(xpath, &rinfo->nh.gate.ipv4);
- default:
- return NULL;
- }
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/state/routes/route/interface
- */
-static struct yang_data *
-ripd_instance_state_routes_route_interface_get_elem(const char *xpath,
- const void *list_entry)
-{
- const struct route_node *rn = list_entry;
- const struct rip_info *rinfo = listnode_head(rn->info);
- const struct rip *rip = rip_info_get_instance(rinfo);
-
- switch (rinfo->nh.type) {
- case NEXTHOP_TYPE_IFINDEX:
- case NEXTHOP_TYPE_IPV4_IFINDEX:
- return yang_data_new_string(
- xpath,
- ifindex2ifname(rinfo->nh.ifindex, rip->vrf->vrf_id));
- default:
- return NULL;
- }
-}
-
-/*
- * XPath: /frr-ripd:ripd/instance/state/routes/route/metric
- */
-static struct yang_data *
-ripd_instance_state_routes_route_metric_get_elem(const char *xpath,
- const void *list_entry)
-{
- const struct route_node *rn = list_entry;
- const struct rip_info *rinfo = listnode_head(rn->info);
-
- return yang_data_new_uint8(xpath, rinfo->metric);
-}
-
-/*
- * XPath: /frr-ripd:clear-rip-route
- */
-static void clear_rip_route(struct rip *rip)
-{
- struct route_node *rp;
-
- if (IS_RIP_DEBUG_EVENT)
- zlog_debug("Clearing all RIP routes (VRF %s)", rip->vrf_name);
-
- /* Clear received RIP routes */
- for (rp = route_top(rip->table); rp; rp = route_next(rp)) {
- struct list *list;
- struct listnode *listnode;
- struct rip_info *rinfo;
-
- list = rp->info;
- if (!list)
- continue;
-
- for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) {
- if (!rip_route_rte(rinfo))
- continue;
-
- if (CHECK_FLAG(rinfo->flags, RIP_RTF_FIB))
- rip_zebra_ipv4_delete(rip, rp);
- break;
- }
-
- if (rinfo) {
- RIP_TIMER_OFF(rinfo->t_timeout);
- RIP_TIMER_OFF(rinfo->t_garbage_collect);
- listnode_delete(list, rinfo);
- rip_info_free(rinfo);
- }
-
- if (list_isempty(list)) {
- list_delete(&list);
- rp->info = NULL;
- route_unlock_node(rp);
- }
- }
-}
-
-static int clear_rip_route_rpc(const char *xpath, const struct list *input,
- struct list *output)
-{
- struct rip *rip;
- struct yang_data *yang_vrf;
-
- yang_vrf = yang_data_list_find(input, "%s/%s", xpath, "input/vrf");
- if (yang_vrf) {
- rip = rip_lookup_by_vrf_name(yang_vrf->value);
- if (rip)
- clear_rip_route(rip);
- } else {
- struct vrf *vrf;
-
- RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
- rip = vrf->info;
- if (!rip)
- continue;
-
- clear_rip_route(rip);
- }
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripd:authentication-type-failure
- */
-void ripd_notif_send_auth_type_failure(const char *ifname)
-{
- const char *xpath = "/frr-ripd:authentication-type-failure";
- struct list *arguments;
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
-
- arguments = yang_data_list_new();
-
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/interface-name", xpath);
- data = yang_data_new_string(xpath_arg, ifname);
- listnode_add(arguments, data);
-
- nb_notification_send(xpath, arguments);
-}
-
-/*
- * XPath: /frr-ripd:authentication-failure
- */
-void ripd_notif_send_auth_failure(const char *ifname)
-{
- const char *xpath = "/frr-ripd:authentication-failure";
- struct list *arguments;
- char xpath_arg[XPATH_MAXLEN];
- struct yang_data *data;
-
- arguments = yang_data_list_new();
-
- snprintf(xpath_arg, sizeof(xpath_arg), "%s/interface-name", xpath);
- data = yang_data_new_string(xpath_arg, ifname);
- listnode_add(arguments, data);
-
- nb_notification_send(xpath, arguments);
-}
-
-/* clang-format off */
-const struct frr_yang_module_info frr_ripd_info = {
- .name = "frr-ripd",
- .nodes = {
- {
- .xpath = "/frr-ripd:ripd/instance",
- .cbs = {
- .cli_show = cli_show_router_rip,
- .create = ripd_instance_create,
- .destroy = ripd_instance_destroy,
- .get_keys = ripd_instance_get_keys,
- .get_next = ripd_instance_get_next,
- .lookup_entry = ripd_instance_lookup_entry,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/allow-ecmp",
- .cbs = {
- .cli_show = cli_show_rip_allow_ecmp,
- .modify = ripd_instance_allow_ecmp_modify,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/default-information-originate",
- .cbs = {
- .cli_show = cli_show_rip_default_information_originate,
- .modify = ripd_instance_default_information_originate_modify,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/default-metric",
- .cbs = {
- .cli_show = cli_show_rip_default_metric,
- .modify = ripd_instance_default_metric_modify,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/distance/default",
- .cbs = {
- .cli_show = cli_show_rip_distance,
- .modify = ripd_instance_distance_default_modify,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/distance/source",
- .cbs = {
- .cli_show = cli_show_rip_distance_source,
- .create = ripd_instance_distance_source_create,
- .destroy = ripd_instance_distance_source_destroy,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/distance/source/distance",
- .cbs = {
- .modify = ripd_instance_distance_source_distance_modify,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/distance/source/access-list",
- .cbs = {
- .destroy = ripd_instance_distance_source_access_list_destroy,
- .modify = ripd_instance_distance_source_access_list_modify,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/explicit-neighbor",
- .cbs = {
- .cli_show = cli_show_rip_neighbor,
- .create = ripd_instance_explicit_neighbor_create,
- .destroy = ripd_instance_explicit_neighbor_destroy,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/network",
- .cbs = {
- .cli_show = cli_show_rip_network_prefix,
- .create = ripd_instance_network_create,
- .destroy = ripd_instance_network_destroy,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/interface",
- .cbs = {
- .cli_show = cli_show_rip_network_interface,
- .create = ripd_instance_interface_create,
- .destroy = ripd_instance_interface_destroy,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/offset-list",
- .cbs = {
- .cli_show = cli_show_rip_offset_list,
- .create = ripd_instance_offset_list_create,
- .destroy = ripd_instance_offset_list_destroy,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/offset-list/access-list",
- .cbs = {
- .modify = ripd_instance_offset_list_access_list_modify,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/offset-list/metric",
- .cbs = {
- .modify = ripd_instance_offset_list_metric_modify,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/passive-default",
- .cbs = {
- .cli_show = cli_show_rip_passive_default,
- .modify = ripd_instance_passive_default_modify,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/passive-interface",
- .cbs = {
- .cli_show = cli_show_rip_passive_interface,
- .create = ripd_instance_passive_interface_create,
- .destroy = ripd_instance_passive_interface_destroy,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/non-passive-interface",
- .cbs = {
- .cli_show = cli_show_rip_non_passive_interface,
- .create = ripd_instance_non_passive_interface_create,
- .destroy = ripd_instance_non_passive_interface_destroy,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/redistribute",
- .cbs = {
- .apply_finish = ripd_instance_redistribute_apply_finish,
- .cli_show = cli_show_rip_redistribute,
- .create = ripd_instance_redistribute_create,
- .destroy = ripd_instance_redistribute_destroy,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/redistribute/route-map",
- .cbs = {
- .destroy = ripd_instance_redistribute_route_map_destroy,
- .modify = ripd_instance_redistribute_route_map_modify,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/redistribute/metric",
- .cbs = {
- .destroy = ripd_instance_redistribute_metric_destroy,
- .modify = ripd_instance_redistribute_metric_modify,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/static-route",
- .cbs = {
- .cli_show = cli_show_rip_route,
- .create = ripd_instance_static_route_create,
- .destroy = ripd_instance_static_route_destroy,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/timers",
- .cbs = {
- .apply_finish = ripd_instance_timers_apply_finish,
- .cli_show = cli_show_rip_timers,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/timers/flush-interval",
- .cbs = {
- .modify = ripd_instance_timers_flush_interval_modify,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/timers/holddown-interval",
- .cbs = {
- .modify = ripd_instance_timers_holddown_interval_modify,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/timers/update-interval",
- .cbs = {
- .modify = ripd_instance_timers_update_interval_modify,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/version",
- .cbs = {
- .cli_show = cli_show_rip_version,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/version/receive",
- .cbs = {
- .modify = ripd_instance_version_receive_modify,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/version/send",
- .cbs = {
- .modify = ripd_instance_version_send_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-ripd:rip/split-horizon",
- .cbs = {
- .cli_show = cli_show_ip_rip_split_horizon,
- .modify = lib_interface_rip_split_horizon_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-ripd:rip/v2-broadcast",
- .cbs = {
- .cli_show = cli_show_ip_rip_v2_broadcast,
- .modify = lib_interface_rip_v2_broadcast_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-ripd:rip/version-receive",
- .cbs = {
- .cli_show = cli_show_ip_rip_receive_version,
- .modify = lib_interface_rip_version_receive_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-ripd:rip/version-send",
- .cbs = {
- .cli_show = cli_show_ip_rip_send_version,
- .modify = lib_interface_rip_version_send_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-scheme",
- .cbs = {
- .cli_show = cli_show_ip_rip_authentication_scheme,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-scheme/mode",
- .cbs = {
- .modify = lib_interface_rip_authentication_scheme_mode_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-scheme/md5-auth-length",
- .cbs = {
- .destroy = lib_interface_rip_authentication_scheme_md5_auth_length_destroy,
- .modify = lib_interface_rip_authentication_scheme_md5_auth_length_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-password",
- .cbs = {
- .cli_show = cli_show_ip_rip_authentication_string,
- .destroy = lib_interface_rip_authentication_password_destroy,
- .modify = lib_interface_rip_authentication_password_modify,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-key-chain",
- .cbs = {
- .cli_show = cli_show_ip_rip_authentication_key_chain,
- .destroy = lib_interface_rip_authentication_key_chain_destroy,
- .modify = lib_interface_rip_authentication_key_chain_modify,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor",
- .cbs = {
- .get_keys = ripd_instance_state_neighbors_neighbor_get_keys,
- .get_next = ripd_instance_state_neighbors_neighbor_get_next,
- .lookup_entry = ripd_instance_state_neighbors_neighbor_lookup_entry,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor/address",
- .cbs = {
- .get_elem = ripd_instance_state_neighbors_neighbor_address_get_elem,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor/last-update",
- .cbs = {
- .get_elem = ripd_instance_state_neighbors_neighbor_last_update_get_elem,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor/bad-packets-rcvd",
- .cbs = {
- .get_elem = ripd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor/bad-routes-rcvd",
- .cbs = {
- .get_elem = ripd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/state/routes/route",
- .cbs = {
- .get_keys = ripd_instance_state_routes_route_get_keys,
- .get_next = ripd_instance_state_routes_route_get_next,
- .lookup_entry = ripd_instance_state_routes_route_lookup_entry,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/state/routes/route/prefix",
- .cbs = {
- .get_elem = ripd_instance_state_routes_route_prefix_get_elem,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/state/routes/route/next-hop",
- .cbs = {
- .get_elem = ripd_instance_state_routes_route_next_hop_get_elem,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/state/routes/route/interface",
- .cbs = {
- .get_elem = ripd_instance_state_routes_route_interface_get_elem,
- },
- },
- {
- .xpath = "/frr-ripd:ripd/instance/state/routes/route/metric",
- .cbs = {
- .get_elem = ripd_instance_state_routes_route_metric_get_elem,
- },
- },
- {
- .xpath = "/frr-ripd:clear-rip-route",
- .cbs = {
- .rpc = clear_rip_route_rpc,
- },
- },
- {
- .xpath = NULL,
- },
- }
-};
diff --git a/ripd/ripd.c b/ripd/ripd.c
index 1b5a582cb1..ec0770ef3d 100644
--- a/ripd/ripd.c
+++ b/ripd/ripd.c
@@ -46,6 +46,7 @@
#include "northbound_cli.h"
#include "ripd/ripd.h"
+#include "ripd/rip_nb.h"
#include "ripd/rip_debug.h"
#include "ripd/rip_errors.h"
#include "ripd/rip_interface.h"
diff --git a/ripd/ripd.h b/ripd/ripd.h
index 7f2c3fd068..417bd5b3b1 100644
--- a/ripd/ripd.h
+++ b/ripd/ripd.h
@@ -521,10 +521,7 @@ extern int offset_list_cmp(struct rip_offset_list *o1,
extern void rip_vrf_init(void);
extern void rip_vrf_terminate(void);
-
-/* YANG notifications */
-extern void ripd_notif_send_auth_type_failure(const char *ifname);
-extern void ripd_notif_send_auth_failure(const char *ifname);
+extern void rip_cli_init(void);
extern struct zebra_privs_t ripd_privs;
extern struct rip_instance_head rip_instances;
@@ -535,8 +532,4 @@ extern struct thread_master *master;
DECLARE_HOOK(rip_ifaddr_add, (struct connected * ifc), (ifc))
DECLARE_HOOK(rip_ifaddr_del, (struct connected * ifc), (ifc))
-/* Northbound. */
-extern void rip_cli_init(void);
-extern const struct frr_yang_module_info frr_ripd_info;
-
#endif /* _ZEBRA_RIP_H */
diff --git a/ripd/subdir.am b/ripd/subdir.am
index 312c1db6af..dfdfc88a56 100644
--- a/ripd/subdir.am
+++ b/ripd/subdir.am
@@ -24,7 +24,11 @@ ripd_librip_a_SOURCES = \
ripd/rip_errors.c \
ripd/rip_interface.c \
ripd/rip_offset.c \
- ripd/rip_northbound.c \
+ ripd/rip_nb.c \
+ ripd/rip_nb_config.c \
+ ripd/rip_nb_rpcs.c \
+ ripd/rip_nb_notifications.c \
+ ripd/rip_nb_state.c \
ripd/rip_peer.c \
ripd/rip_routemap.c \
ripd/rip_zebra.c \
@@ -35,10 +39,10 @@ ripd/rip_cli_clippy.c: $(CLIPPY_DEPS)
ripd/rip_cli.$(OBJEXT): ripd/rip_cli_clippy.c
noinst_HEADERS += \
- ripd/rip_cli.h \
ripd/rip_debug.h \
ripd/rip_errors.h \
ripd/rip_interface.h \
+ ripd/rip_nb.h \
ripd/ripd.h \
# end
diff --git a/ripngd/ripng_cli.c b/ripngd/ripng_cli.c
index e95c0e95d6..2d9930e357 100644
--- a/ripngd/ripng_cli.c
+++ b/ripngd/ripng_cli.c
@@ -29,7 +29,7 @@
#include "libfrr.h"
#include "ripngd/ripngd.h"
-#include "ripngd/ripng_cli.h"
+#include "ripngd/ripng_nb.h"
#ifndef VTYSH_EXTRACT_PL
#include "ripngd/ripng_cli_clippy.c"
#endif
diff --git a/ripngd/ripng_cli.h b/ripngd/ripng_cli.h
deleted file mode 100644
index d95747e0f8..0000000000
--- a/ripngd/ripng_cli.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 1998 Kunihiro Ishiguro
- * Copyright (C) 2018 NetDEF, Inc.
- * Renato Westphal
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; see the file COPYING; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef _FRR_RIPNG_CLI_H_
-#define _FRR_RIPNG_CLI_H_
-
-extern void cli_show_router_ripng(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_ripng_allow_ecmp(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_ripng_default_information_originate(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_ripng_default_metric(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_ripng_network_prefix(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_ripng_network_interface(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_ripng_offset_list(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_ripng_passive_interface(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_ripng_redistribute(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_ripng_route(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_ripng_aggregate_address(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_ripng_timers(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void cli_show_ipv6_ripng_split_horizon(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-
-#endif /* _FRR_RIPNG_CLI_H_ */
diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c
index 9209a76460..97113a180f 100644
--- a/ripngd/ripng_interface.c
+++ b/ripngd/ripng_interface.c
@@ -430,7 +430,7 @@ int ripng_interface_address_delete(ZAPI_CALLBACK_ARGS)
/* Check wether this prefix needs to be removed. */
ripng_apply_address_del(ifc);
}
- connected_free(ifc);
+ connected_free(&ifc);
}
return 0;
diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c
index 4b027019c0..3f2c0e9da0 100644
--- a/ripngd/ripng_main.c
+++ b/ripngd/ripng_main.c
@@ -39,6 +39,7 @@
#include "libfrr.h"
#include "ripngd/ripngd.h"
+#include "ripngd/ripng_nb.h"
/* RIPngd options. */
struct option longopts[] = {{0}};
diff --git a/ripngd/ripng_nb.c b/ripngd/ripng_nb.c
new file mode 100644
index 0000000000..a02a72112f
--- /dev/null
+++ b/ripngd/ripng_nb.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "northbound.h"
+#include "libfrr.h"
+
+#include "ripngd/ripng_nb.h"
+
+/* clang-format off */
+const struct frr_yang_module_info frr_ripngd_info = {
+ .name = "frr-ripngd",
+ .nodes = {
+ {
+ .xpath = "/frr-ripngd:ripngd/instance",
+ .cbs = {
+ .cli_show = cli_show_router_ripng,
+ .create = ripngd_instance_create,
+ .destroy = ripngd_instance_destroy,
+ .get_keys = ripngd_instance_get_keys,
+ .get_next = ripngd_instance_get_next,
+ .lookup_entry = ripngd_instance_lookup_entry,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/allow-ecmp",
+ .cbs = {
+ .cli_show = cli_show_ripng_allow_ecmp,
+ .modify = ripngd_instance_allow_ecmp_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/default-information-originate",
+ .cbs = {
+ .cli_show = cli_show_ripng_default_information_originate,
+ .modify = ripngd_instance_default_information_originate_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/default-metric",
+ .cbs = {
+ .cli_show = cli_show_ripng_default_metric,
+ .modify = ripngd_instance_default_metric_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/network",
+ .cbs = {
+ .cli_show = cli_show_ripng_network_prefix,
+ .create = ripngd_instance_network_create,
+ .destroy = ripngd_instance_network_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/interface",
+ .cbs = {
+ .cli_show = cli_show_ripng_network_interface,
+ .create = ripngd_instance_interface_create,
+ .destroy = ripngd_instance_interface_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/offset-list",
+ .cbs = {
+ .cli_show = cli_show_ripng_offset_list,
+ .create = ripngd_instance_offset_list_create,
+ .destroy = ripngd_instance_offset_list_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/offset-list/access-list",
+ .cbs = {
+ .modify = ripngd_instance_offset_list_access_list_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/offset-list/metric",
+ .cbs = {
+ .modify = ripngd_instance_offset_list_metric_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/passive-interface",
+ .cbs = {
+ .cli_show = cli_show_ripng_passive_interface,
+ .create = ripngd_instance_passive_interface_create,
+ .destroy = ripngd_instance_passive_interface_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/redistribute",
+ .cbs = {
+ .apply_finish = ripngd_instance_redistribute_apply_finish,
+ .cli_show = cli_show_ripng_redistribute,
+ .create = ripngd_instance_redistribute_create,
+ .destroy = ripngd_instance_redistribute_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/redistribute/route-map",
+ .cbs = {
+ .destroy = ripngd_instance_redistribute_route_map_destroy,
+ .modify = ripngd_instance_redistribute_route_map_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/redistribute/metric",
+ .cbs = {
+ .destroy = ripngd_instance_redistribute_metric_destroy,
+ .modify = ripngd_instance_redistribute_metric_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/static-route",
+ .cbs = {
+ .cli_show = cli_show_ripng_route,
+ .create = ripngd_instance_static_route_create,
+ .destroy = ripngd_instance_static_route_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/aggregate-address",
+ .cbs = {
+ .cli_show = cli_show_ripng_aggregate_address,
+ .create = ripngd_instance_aggregate_address_create,
+ .destroy = ripngd_instance_aggregate_address_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/timers",
+ .cbs = {
+ .apply_finish = ripngd_instance_timers_apply_finish,
+ .cli_show = cli_show_ripng_timers,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/timers/flush-interval",
+ .cbs = {
+ .modify = ripngd_instance_timers_flush_interval_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/timers/holddown-interval",
+ .cbs = {
+ .modify = ripngd_instance_timers_holddown_interval_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/timers/update-interval",
+ .cbs = {
+ .modify = ripngd_instance_timers_update_interval_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor",
+ .cbs = {
+ .get_keys = ripngd_instance_state_neighbors_neighbor_get_keys,
+ .get_next = ripngd_instance_state_neighbors_neighbor_get_next,
+ .lookup_entry = ripngd_instance_state_neighbors_neighbor_lookup_entry,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor/address",
+ .cbs = {
+ .get_elem = ripngd_instance_state_neighbors_neighbor_address_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor/last-update",
+ .cbs = {
+ .get_elem = ripngd_instance_state_neighbors_neighbor_last_update_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor/bad-packets-rcvd",
+ .cbs = {
+ .get_elem = ripngd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor/bad-routes-rcvd",
+ .cbs = {
+ .get_elem = ripngd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/state/routes/route",
+ .cbs = {
+ .get_keys = ripngd_instance_state_routes_route_get_keys,
+ .get_next = ripngd_instance_state_routes_route_get_next,
+ .lookup_entry = ripngd_instance_state_routes_route_lookup_entry,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/state/routes/route/prefix",
+ .cbs = {
+ .get_elem = ripngd_instance_state_routes_route_prefix_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/state/routes/route/next-hop",
+ .cbs = {
+ .get_elem = ripngd_instance_state_routes_route_next_hop_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/state/routes/route/interface",
+ .cbs = {
+ .get_elem = ripngd_instance_state_routes_route_interface_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/state/routes/route/metric",
+ .cbs = {
+ .get_elem = ripngd_instance_state_routes_route_metric_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:clear-ripng-route",
+ .cbs = {
+ .rpc = clear_ripng_route_rpc,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-ripngd:ripng/split-horizon",
+ .cbs = {
+ .cli_show = cli_show_ipv6_ripng_split_horizon,
+ .modify = lib_interface_ripng_split_horizon_modify,
+ },
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
diff --git a/ripngd/ripng_nb.h b/ripngd/ripng_nb.h
new file mode 100644
index 0000000000..45e92e0a10
--- /dev/null
+++ b/ripngd/ripng_nb.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_RIPNG_NB_H_
+#define _FRR_RIPNG_NB_H_
+
+extern const struct frr_yang_module_info frr_ripngd_info;
+
+/* Mandatory callbacks. */
+int ripngd_instance_create(enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripngd_instance_destroy(enum nb_event event, const struct lyd_node *dnode);
+const void *ripngd_instance_get_next(const void *parent_list_entry,
+ const void *list_entry);
+int ripngd_instance_get_keys(const void *list_entry,
+ struct yang_list_keys *keys);
+const void *ripngd_instance_lookup_entry(const void *parent_list_entry,
+ const struct yang_list_keys *keys);
+int ripngd_instance_allow_ecmp_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripngd_instance_default_information_originate_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripngd_instance_default_metric_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripngd_instance_network_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripngd_instance_network_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int ripngd_instance_interface_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripngd_instance_interface_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int ripngd_instance_offset_list_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripngd_instance_offset_list_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int ripngd_instance_offset_list_access_list_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripngd_instance_offset_list_metric_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripngd_instance_passive_interface_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripngd_instance_passive_interface_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int ripngd_instance_redistribute_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripngd_instance_redistribute_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int ripngd_instance_redistribute_route_map_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripngd_instance_redistribute_route_map_destroy(
+ enum nb_event event, const struct lyd_node *dnode);
+int ripngd_instance_redistribute_metric_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripngd_instance_redistribute_metric_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int ripngd_instance_static_route_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripngd_instance_static_route_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int ripngd_instance_aggregate_address_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripngd_instance_aggregate_address_destroy(enum nb_event event,
+ const struct lyd_node *dnode);
+int ripngd_instance_timers_flush_interval_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripngd_instance_timers_holddown_interval_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+int ripngd_instance_timers_update_interval_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+const void *
+ripngd_instance_state_neighbors_neighbor_get_next(const void *parent_list_entry,
+ const void *list_entry);
+int ripngd_instance_state_neighbors_neighbor_get_keys(
+ const void *list_entry, struct yang_list_keys *keys);
+const void *ripngd_instance_state_neighbors_neighbor_lookup_entry(
+ const void *parent_list_entry, const struct yang_list_keys *keys);
+struct yang_data *ripngd_instance_state_neighbors_neighbor_address_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *ripngd_instance_state_neighbors_neighbor_last_update_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+ripngd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem(
+ const char *xpath, const void *list_entry);
+struct yang_data *
+ripngd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem(
+ const char *xpath, const void *list_entry);
+const void *
+ripngd_instance_state_routes_route_get_next(const void *parent_list_entry,
+ const void *list_entry);
+int ripngd_instance_state_routes_route_get_keys(const void *list_entry,
+ struct yang_list_keys *keys);
+const void *ripngd_instance_state_routes_route_lookup_entry(
+ const void *parent_list_entry, const struct yang_list_keys *keys);
+struct yang_data *
+ripngd_instance_state_routes_route_prefix_get_elem(const char *xpath,
+ const void *list_entry);
+struct yang_data *
+ripngd_instance_state_routes_route_next_hop_get_elem(const char *xpath,
+ const void *list_entry);
+struct yang_data *
+ripngd_instance_state_routes_route_interface_get_elem(const char *xpath,
+ const void *list_entry);
+struct yang_data *
+ripngd_instance_state_routes_route_metric_get_elem(const char *xpath,
+ const void *list_entry);
+int clear_ripng_route_rpc(const char *xpath, const struct list *input,
+ struct list *output);
+int lib_interface_ripng_split_horizon_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource);
+
+/* Optional 'apply_finish' callbacks. */
+void ripngd_instance_redistribute_apply_finish(const struct lyd_node *dnode);
+void ripngd_instance_timers_apply_finish(const struct lyd_node *dnode);
+
+/* Optional 'cli_show' callbacks. */
+void cli_show_router_ripng(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_allow_ecmp(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_default_information_originate(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_default_metric(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_network_prefix(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_network_interface(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_offset_list(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_passive_interface(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_redistribute(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_route(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_aggregate_address(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_timers(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ipv6_ripng_split_horizon(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+
+#endif /* _FRR_RIPNG_NB_H_ */
diff --git a/ripngd/ripng_nb_config.c b/ripngd/ripng_nb_config.c
new file mode 100644
index 0000000000..b39c1d443a
--- /dev/null
+++ b/ripngd/ripng_nb_config.c
@@ -0,0 +1,714 @@
+/*
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "vrf.h"
+#include "log.h"
+#include "prefix.h"
+#include "table.h"
+#include "command.h"
+#include "routemap.h"
+#include "agg_table.h"
+#include "northbound.h"
+#include "libfrr.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_nb.h"
+#include "ripngd/ripng_debug.h"
+#include "ripngd/ripng_route.h"
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance
+ */
+int ripngd_instance_create(enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct ripng *ripng;
+ struct vrf *vrf;
+ const char *vrf_name;
+ int socket;
+
+ vrf_name = yang_dnode_get_string(dnode, "./vrf");
+ vrf = vrf_lookup_by_name(vrf_name);
+
+ /*
+ * Try to create a RIPng socket only if the VRF is enabled, otherwise
+ * create a disabled RIPng instance and wait for the VRF to be enabled.
+ */
+ switch (event) {
+ case NB_EV_VALIDATE:
+ break;
+ case NB_EV_PREPARE:
+ if (!vrf || !vrf_is_enabled(vrf))
+ break;
+
+ socket = ripng_make_socket(vrf);
+ if (socket < 0)
+ return NB_ERR_RESOURCE;
+ resource->fd = socket;
+ break;
+ case NB_EV_ABORT:
+ if (!vrf || !vrf_is_enabled(vrf))
+ break;
+
+ socket = resource->fd;
+ close(socket);
+ break;
+ case NB_EV_APPLY:
+ if (vrf && vrf_is_enabled(vrf))
+ socket = resource->fd;
+ else
+ socket = -1;
+
+ ripng = ripng_create(vrf_name, vrf, socket);
+ nb_running_set_entry(dnode, ripng);
+ break;
+ }
+
+ return NB_OK;
+}
+
+int ripngd_instance_destroy(enum nb_event event, const struct lyd_node *dnode)
+{
+ struct ripng *ripng;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_unset_entry(dnode);
+ ripng_clean(ripng);
+
+ return NB_OK;
+}
+
+const void *ripngd_instance_get_next(const void *parent_list_entry,
+ const void *list_entry)
+{
+ struct ripng *ripng = (struct ripng *)list_entry;
+
+ if (list_entry == NULL)
+ ripng = RB_MIN(ripng_instance_head, &ripng_instances);
+ else
+ ripng = RB_NEXT(ripng_instance_head, ripng);
+
+ return ripng;
+}
+
+int ripngd_instance_get_keys(const void *list_entry,
+ struct yang_list_keys *keys)
+{
+ const struct ripng *ripng = list_entry;
+
+ keys->num = 1;
+ strlcpy(keys->key[0], ripng->vrf_name, sizeof(keys->key[0]));
+
+ return NB_OK;
+}
+
+const void *ripngd_instance_lookup_entry(const void *parent_list_entry,
+ const struct yang_list_keys *keys)
+{
+ const char *vrf_name = keys->key[0];
+
+ return ripng_lookup_by_vrf_name(vrf_name);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/allow-ecmp
+ */
+int ripngd_instance_allow_ecmp_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct ripng *ripng;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ ripng->ecmp = yang_dnode_get_bool(dnode, NULL);
+ if (!ripng->ecmp)
+ ripng_ecmp_disable(ripng);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/default-information-originate
+ */
+int ripngd_instance_default_information_originate_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct ripng *ripng;
+ bool default_information;
+ struct prefix_ipv6 p;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ default_information = yang_dnode_get_bool(dnode, NULL);
+
+ str2prefix_ipv6("::/0", &p);
+ if (default_information) {
+ ripng_redistribute_add(ripng, ZEBRA_ROUTE_RIPNG,
+ RIPNG_ROUTE_DEFAULT, &p, 0, NULL, 0);
+ } else {
+ ripng_redistribute_delete(ripng, ZEBRA_ROUTE_RIPNG,
+ RIPNG_ROUTE_DEFAULT, &p, 0);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/default-metric
+ */
+int ripngd_instance_default_metric_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct ripng *ripng;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ ripng->default_metric = yang_dnode_get_uint8(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/network
+ */
+int ripngd_instance_network_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct ripng *ripng;
+ struct prefix p;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ yang_dnode_get_ipv6p(&p, dnode, NULL);
+ apply_mask_ipv6((struct prefix_ipv6 *)&p);
+
+ return ripng_enable_network_add(ripng, &p);
+}
+
+int ripngd_instance_network_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct ripng *ripng;
+ struct prefix p;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ yang_dnode_get_ipv6p(&p, dnode, NULL);
+ apply_mask_ipv6((struct prefix_ipv6 *)&p);
+
+ return ripng_enable_network_delete(ripng, &p);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/interface
+ */
+int ripngd_instance_interface_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct ripng *ripng;
+ const char *ifname;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ ifname = yang_dnode_get_string(dnode, NULL);
+
+ return ripng_enable_if_add(ripng, ifname);
+}
+
+int ripngd_instance_interface_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct ripng *ripng;
+ const char *ifname;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ ifname = yang_dnode_get_string(dnode, NULL);
+
+ return ripng_enable_if_delete(ripng, ifname);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/offset-list
+ */
+int ripngd_instance_offset_list_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct ripng *ripng;
+ const char *ifname;
+ struct ripng_offset_list *offset;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ ifname = yang_dnode_get_string(dnode, "./interface");
+
+ offset = ripng_offset_list_new(ripng, ifname);
+ nb_running_set_entry(dnode, offset);
+
+ return NB_OK;
+}
+
+int ripngd_instance_offset_list_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ int direct;
+ struct ripng_offset_list *offset;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ direct = yang_dnode_get_enum(dnode, "./direction");
+
+ offset = nb_running_unset_entry(dnode);
+ if (offset->direct[direct].alist_name) {
+ free(offset->direct[direct].alist_name);
+ offset->direct[direct].alist_name = NULL;
+ }
+ if (offset->direct[RIPNG_OFFSET_LIST_IN].alist_name == NULL
+ && offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name == NULL)
+ ripng_offset_list_del(offset);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/offset-list/access-list
+ */
+int ripngd_instance_offset_list_access_list_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ int direct;
+ struct ripng_offset_list *offset;
+ const char *alist_name;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ direct = yang_dnode_get_enum(dnode, "../direction");
+ alist_name = yang_dnode_get_string(dnode, NULL);
+
+ offset = nb_running_get_entry(dnode, NULL, true);
+ if (offset->direct[direct].alist_name)
+ free(offset->direct[direct].alist_name);
+ offset->direct[direct].alist_name = strdup(alist_name);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/offset-list/metric
+ */
+int ripngd_instance_offset_list_metric_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ int direct;
+ uint8_t metric;
+ struct ripng_offset_list *offset;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ direct = yang_dnode_get_enum(dnode, "../direction");
+ metric = yang_dnode_get_uint8(dnode, NULL);
+
+ offset = nb_running_get_entry(dnode, NULL, true);
+ offset->direct[direct].metric = metric;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/passive-interface
+ */
+int ripngd_instance_passive_interface_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct ripng *ripng;
+ const char *ifname;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ ifname = yang_dnode_get_string(dnode, NULL);
+
+ return ripng_passive_interface_set(ripng, ifname);
+}
+
+int ripngd_instance_passive_interface_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct ripng *ripng;
+ const char *ifname;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ ifname = yang_dnode_get_string(dnode, NULL);
+
+ return ripng_passive_interface_unset(ripng, ifname);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/redistribute
+ */
+int ripngd_instance_redistribute_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct ripng *ripng;
+ int type;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ type = yang_dnode_get_enum(dnode, "./protocol");
+
+ ripng->redist[type].enabled = true;
+
+ return NB_OK;
+}
+
+int ripngd_instance_redistribute_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct ripng *ripng;
+ int type;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ type = yang_dnode_get_enum(dnode, "./protocol");
+
+ ripng->redist[type].enabled = false;
+ if (ripng->redist[type].route_map.name) {
+ free(ripng->redist[type].route_map.name);
+ ripng->redist[type].route_map.name = NULL;
+ ripng->redist[type].route_map.map = NULL;
+ }
+ ripng->redist[type].metric_config = false;
+ ripng->redist[type].metric = 0;
+
+ if (ripng->enabled)
+ ripng_redistribute_conf_delete(ripng, type);
+
+ return NB_OK;
+}
+
+void ripngd_instance_redistribute_apply_finish(const struct lyd_node *dnode)
+{
+ struct ripng *ripng;
+ int type;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ type = yang_dnode_get_enum(dnode, "./protocol");
+
+ if (ripng->enabled)
+ ripng_redistribute_conf_update(ripng, type);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/redistribute/route-map
+ */
+int ripngd_instance_redistribute_route_map_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct ripng *ripng;
+ int type;
+ const char *rmap_name;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ type = yang_dnode_get_enum(dnode, "../protocol");
+ rmap_name = yang_dnode_get_string(dnode, NULL);
+
+ if (ripng->redist[type].route_map.name)
+ free(ripng->redist[type].route_map.name);
+ ripng->redist[type].route_map.name = strdup(rmap_name);
+ ripng->redist[type].route_map.map = route_map_lookup_by_name(rmap_name);
+
+ return NB_OK;
+}
+
+int ripngd_instance_redistribute_route_map_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct ripng *ripng;
+ int type;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ type = yang_dnode_get_enum(dnode, "../protocol");
+
+ free(ripng->redist[type].route_map.name);
+ ripng->redist[type].route_map.name = NULL;
+ ripng->redist[type].route_map.map = NULL;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/redistribute/metric
+ */
+int ripngd_instance_redistribute_metric_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct ripng *ripng;
+ int type;
+ uint8_t metric;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ type = yang_dnode_get_enum(dnode, "../protocol");
+ metric = yang_dnode_get_uint8(dnode, NULL);
+
+ ripng->redist[type].metric_config = true;
+ ripng->redist[type].metric = metric;
+
+ return NB_OK;
+}
+
+int ripngd_instance_redistribute_metric_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct ripng *ripng;
+ int type;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ type = yang_dnode_get_enum(dnode, "../protocol");
+
+ ripng->redist[type].metric_config = false;
+ ripng->redist[type].metric = 0;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/static-route
+ */
+int ripngd_instance_static_route_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct ripng *ripng;
+ struct prefix_ipv6 p;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ yang_dnode_get_ipv6p(&p, dnode, NULL);
+ apply_mask_ipv6(&p);
+
+ ripng_redistribute_add(ripng, ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC, &p,
+ 0, NULL, 0);
+
+ return NB_OK;
+}
+
+int ripngd_instance_static_route_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct ripng *ripng;
+ struct prefix_ipv6 p;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ yang_dnode_get_ipv6p(&p, dnode, NULL);
+ apply_mask_ipv6(&p);
+
+ ripng_redistribute_delete(ripng, ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC,
+ &p, 0);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/aggregate-address
+ */
+int ripngd_instance_aggregate_address_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct ripng *ripng;
+ struct prefix_ipv6 p;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ yang_dnode_get_ipv6p(&p, dnode, NULL);
+ apply_mask_ipv6(&p);
+
+ ripng_aggregate_add(ripng, (struct prefix *)&p);
+
+ return NB_OK;
+}
+
+int ripngd_instance_aggregate_address_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct ripng *ripng;
+ struct prefix_ipv6 p;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ yang_dnode_get_ipv6p(&p, dnode, NULL);
+ apply_mask_ipv6(&p);
+
+ ripng_aggregate_delete(ripng, (struct prefix *)&p);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/timers
+ */
+void ripngd_instance_timers_apply_finish(const struct lyd_node *dnode)
+{
+ struct ripng *ripng;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+
+ /* Reset update timer thread. */
+ ripng_event(ripng, RIPNG_UPDATE_EVENT, 0);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/timers/flush-interval
+ */
+int ripngd_instance_timers_flush_interval_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct ripng *ripng;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ ripng->garbage_time = yang_dnode_get_uint16(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/timers/holddown-interval
+ */
+int ripngd_instance_timers_holddown_interval_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct ripng *ripng;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ ripng->timeout_time = yang_dnode_get_uint16(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/timers/update-interval
+ */
+int ripngd_instance_timers_update_interval_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct ripng *ripng;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(dnode, NULL, true);
+ ripng->update_time = yang_dnode_get_uint16(dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripngd:ripng/split-horizon
+ */
+int lib_interface_ripng_split_horizon_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct interface *ifp;
+ struct ripng_interface *ri;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(dnode, NULL, true);
+ ri = ifp->info;
+ ri->split_horizon = yang_dnode_get_enum(dnode, NULL);
+
+ return NB_OK;
+}
diff --git a/ripngd/ripng_nb_rpcs.c b/ripngd/ripng_nb_rpcs.c
new file mode 100644
index 0000000000..0396daf890
--- /dev/null
+++ b/ripngd/ripng_nb_rpcs.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "vrf.h"
+#include "log.h"
+#include "prefix.h"
+#include "table.h"
+#include "command.h"
+#include "routemap.h"
+#include "agg_table.h"
+#include "northbound.h"
+#include "libfrr.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_nb.h"
+#include "ripngd/ripng_debug.h"
+#include "ripngd/ripng_route.h"
+
+/*
+ * XPath: /frr-ripngd:clear-ripng-route
+ */
+static void clear_ripng_route(struct ripng *ripng)
+{
+ struct agg_node *rp;
+
+ if (IS_RIPNG_DEBUG_EVENT)
+ zlog_debug("Clearing all RIPng routes (VRF %s)",
+ ripng->vrf_name);
+
+ /* Clear received RIPng routes */
+ for (rp = agg_route_top(ripng->table); rp; rp = agg_route_next(rp)) {
+ struct list *list;
+ struct listnode *listnode;
+ struct ripng_info *rinfo;
+
+ list = rp->info;
+ if (list == NULL)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) {
+ if (!ripng_route_rte(rinfo))
+ continue;
+
+ if (CHECK_FLAG(rinfo->flags, RIPNG_RTF_FIB))
+ ripng_zebra_ipv6_delete(ripng, rp);
+ break;
+ }
+
+ if (rinfo) {
+ RIPNG_TIMER_OFF(rinfo->t_timeout);
+ RIPNG_TIMER_OFF(rinfo->t_garbage_collect);
+ listnode_delete(list, rinfo);
+ ripng_info_free(rinfo);
+ }
+
+ if (list_isempty(list)) {
+ list_delete(&list);
+ rp->info = NULL;
+ agg_unlock_node(rp);
+ }
+ }
+}
+
+int clear_ripng_route_rpc(const char *xpath, const struct list *input,
+ struct list *output)
+{
+ struct ripng *ripng;
+ struct yang_data *yang_vrf;
+
+ yang_vrf = yang_data_list_find(input, "%s/%s", xpath, "input/vrf");
+ if (yang_vrf) {
+ ripng = ripng_lookup_by_vrf_name(yang_vrf->value);
+ if (ripng)
+ clear_ripng_route(ripng);
+ } else {
+ struct vrf *vrf;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ ripng = vrf->info;
+ if (!ripng)
+ continue;
+
+ clear_ripng_route(ripng);
+ }
+ }
+
+ return NB_OK;
+}
diff --git a/ripngd/ripng_nb_state.c b/ripngd/ripng_nb_state.c
new file mode 100644
index 0000000000..167077ea29
--- /dev/null
+++ b/ripngd/ripng_nb_state.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "vrf.h"
+#include "log.h"
+#include "prefix.h"
+#include "table.h"
+#include "command.h"
+#include "routemap.h"
+#include "agg_table.h"
+#include "northbound.h"
+#include "libfrr.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_nb.h"
+#include "ripngd/ripng_debug.h"
+#include "ripngd/ripng_route.h"
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor
+ */
+const void *
+ripngd_instance_state_neighbors_neighbor_get_next(const void *parent_list_entry,
+ const void *list_entry)
+{
+ const struct ripng *ripng = parent_list_entry;
+ struct listnode *node;
+
+ if (list_entry == NULL)
+ node = listhead(ripng->peer_list);
+ else
+ node = listnextnode((struct listnode *)list_entry);
+
+ return node;
+}
+
+int ripngd_instance_state_neighbors_neighbor_get_keys(
+ const void *list_entry, struct yang_list_keys *keys)
+{
+ const struct listnode *node = list_entry;
+ const struct ripng_peer *peer = listgetdata(node);
+
+ keys->num = 1;
+ (void)inet_ntop(AF_INET6, &peer->addr, keys->key[0],
+ sizeof(keys->key[0]));
+
+ return NB_OK;
+}
+
+const void *ripngd_instance_state_neighbors_neighbor_lookup_entry(
+ const void *parent_list_entry, const struct yang_list_keys *keys)
+{
+ const struct ripng *ripng = parent_list_entry;
+ struct in6_addr address;
+ struct ripng_peer *peer;
+ struct listnode *node;
+
+ yang_str2ipv6(keys->key[0], &address);
+
+ for (ALL_LIST_ELEMENTS_RO(ripng->peer_list, node, peer)) {
+ if (IPV6_ADDR_SAME(&peer->addr, &address))
+ return node;
+ }
+
+ return NULL;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor/address
+ */
+struct yang_data *ripngd_instance_state_neighbors_neighbor_address_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct listnode *node = list_entry;
+ const struct ripng_peer *peer = listgetdata(node);
+
+ return yang_data_new_ipv6(xpath, &peer->addr);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor/last-update
+ */
+struct yang_data *ripngd_instance_state_neighbors_neighbor_last_update_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ /* TODO: yang:date-and-time is tricky */
+ return NULL;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor/bad-packets-rcvd
+ */
+struct yang_data *
+ripngd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct listnode *node = list_entry;
+ const struct ripng_peer *peer = listgetdata(node);
+
+ return yang_data_new_uint32(xpath, peer->recv_badpackets);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor/bad-routes-rcvd
+ */
+struct yang_data *
+ripngd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct listnode *node = list_entry;
+ const struct ripng_peer *peer = listgetdata(node);
+
+ return yang_data_new_uint32(xpath, peer->recv_badroutes);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/state/routes/route
+ */
+const void *
+ripngd_instance_state_routes_route_get_next(const void *parent_list_entry,
+ const void *list_entry)
+{
+ const struct ripng *ripng = parent_list_entry;
+ struct agg_node *rn;
+
+ if (list_entry == NULL)
+ rn = agg_route_top(ripng->table);
+ else
+ rn = agg_route_next((struct agg_node *)list_entry);
+ while (rn && rn->info == NULL)
+ rn = agg_route_next(rn);
+
+ return rn;
+}
+
+int ripngd_instance_state_routes_route_get_keys(const void *list_entry,
+ struct yang_list_keys *keys)
+{
+ const struct agg_node *rn = list_entry;
+
+ keys->num = 1;
+ (void)prefix2str(&rn->p, keys->key[0], sizeof(keys->key[0]));
+
+ return NB_OK;
+}
+
+const void *ripngd_instance_state_routes_route_lookup_entry(
+ const void *parent_list_entry, const struct yang_list_keys *keys)
+{
+ const struct ripng *ripng = parent_list_entry;
+ struct prefix prefix;
+ struct agg_node *rn;
+
+ yang_str2ipv6p(keys->key[0], &prefix);
+
+ rn = agg_node_lookup(ripng->table, &prefix);
+ if (!rn || !rn->info)
+ return NULL;
+
+ agg_unlock_node(rn);
+
+ return rn;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/state/routes/route/prefix
+ */
+struct yang_data *
+ripngd_instance_state_routes_route_prefix_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct agg_node *rn = list_entry;
+ const struct ripng_info *rinfo = listnode_head(rn->info);
+
+ return yang_data_new_ipv6p(xpath, &rinfo->rp->p);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/state/routes/route/next-hop
+ */
+struct yang_data *
+ripngd_instance_state_routes_route_next_hop_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct agg_node *rn = list_entry;
+ const struct ripng_info *rinfo = listnode_head(rn->info);
+
+ return yang_data_new_ipv6(xpath, &rinfo->nexthop);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/state/routes/route/interface
+ */
+struct yang_data *
+ripngd_instance_state_routes_route_interface_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct agg_node *rn = list_entry;
+ const struct ripng_info *rinfo = listnode_head(rn->info);
+ const struct ripng *ripng = ripng_info_get_instance(rinfo);
+
+ return yang_data_new_string(
+ xpath, ifindex2ifname(rinfo->ifindex, ripng->vrf->vrf_id));
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/state/routes/route/metric
+ */
+struct yang_data *
+ripngd_instance_state_routes_route_metric_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct agg_node *rn = list_entry;
+ const struct ripng_info *rinfo = listnode_head(rn->info);
+
+ return yang_data_new_uint8(xpath, rinfo->metric);
+}
diff --git a/ripngd/ripng_northbound.c b/ripngd/ripng_northbound.c
deleted file mode 100644
index 588f6db037..0000000000
--- a/ripngd/ripng_northbound.c
+++ /dev/null
@@ -1,1234 +0,0 @@
-/*
- * Copyright (C) 1998 Kunihiro Ishiguro
- * Copyright (C) 2018 NetDEF, Inc.
- * Renato Westphal
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; see the file COPYING; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <zebra.h>
-
-#include "if.h"
-#include "vrf.h"
-#include "log.h"
-#include "prefix.h"
-#include "table.h"
-#include "command.h"
-#include "routemap.h"
-#include "agg_table.h"
-#include "northbound.h"
-#include "libfrr.h"
-
-#include "ripngd/ripngd.h"
-#include "ripngd/ripng_debug.h"
-#include "ripngd/ripng_route.h"
-#include "ripngd/ripng_cli.h"
-
-/*
- * XPath: /frr-ripngd:ripngd/instance
- */
-static int ripngd_instance_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct ripng *ripng;
- struct vrf *vrf;
- const char *vrf_name;
- int socket;
-
- vrf_name = yang_dnode_get_string(dnode, "./vrf");
- vrf = vrf_lookup_by_name(vrf_name);
-
- /*
- * Try to create a RIPng socket only if the VRF is enabled, otherwise
- * create a disabled RIPng instance and wait for the VRF to be enabled.
- */
- switch (event) {
- case NB_EV_VALIDATE:
- break;
- case NB_EV_PREPARE:
- if (!vrf || !vrf_is_enabled(vrf))
- break;
-
- socket = ripng_make_socket(vrf);
- if (socket < 0)
- return NB_ERR_RESOURCE;
- resource->fd = socket;
- break;
- case NB_EV_ABORT:
- if (!vrf || !vrf_is_enabled(vrf))
- break;
-
- socket = resource->fd;
- close(socket);
- break;
- case NB_EV_APPLY:
- if (vrf && vrf_is_enabled(vrf))
- socket = resource->fd;
- else
- socket = -1;
-
- ripng = ripng_create(vrf_name, vrf, socket);
- nb_running_set_entry(dnode, ripng);
- break;
- }
-
- return NB_OK;
-}
-
-static int ripngd_instance_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct ripng *ripng;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_unset_entry(dnode);
- ripng_clean(ripng);
-
- return NB_OK;
-}
-
-static const void *ripngd_instance_get_next(const void *parent_list_entry,
- const void *list_entry)
-{
- struct ripng *ripng = (struct ripng *)list_entry;
-
- if (list_entry == NULL)
- ripng = RB_MIN(ripng_instance_head, &ripng_instances);
- else
- ripng = RB_NEXT(ripng_instance_head, ripng);
-
- return ripng;
-}
-
-static int ripngd_instance_get_keys(const void *list_entry,
- struct yang_list_keys *keys)
-{
- const struct ripng *ripng = list_entry;
-
- keys->num = 1;
- strlcpy(keys->key[0], ripng->vrf_name, sizeof(keys->key[0]));
-
- return NB_OK;
-}
-
-static const void *
-ripngd_instance_lookup_entry(const void *parent_list_entry,
- const struct yang_list_keys *keys)
-{
- const char *vrf_name = keys->key[0];
-
- return ripng_lookup_by_vrf_name(vrf_name);
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/allow-ecmp
- */
-static int ripngd_instance_allow_ecmp_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct ripng *ripng;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- ripng->ecmp = yang_dnode_get_bool(dnode, NULL);
- if (!ripng->ecmp)
- ripng_ecmp_disable(ripng);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/default-information-originate
- */
-static int ripngd_instance_default_information_originate_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct ripng *ripng;
- bool default_information;
- struct prefix_ipv6 p;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- default_information = yang_dnode_get_bool(dnode, NULL);
-
- str2prefix_ipv6("::/0", &p);
- if (default_information) {
- ripng_redistribute_add(ripng, ZEBRA_ROUTE_RIPNG,
- RIPNG_ROUTE_DEFAULT, &p, 0, NULL, 0);
- } else {
- ripng_redistribute_delete(ripng, ZEBRA_ROUTE_RIPNG,
- RIPNG_ROUTE_DEFAULT, &p, 0);
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/default-metric
- */
-static int ripngd_instance_default_metric_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct ripng *ripng;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- ripng->default_metric = yang_dnode_get_uint8(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/network
- */
-static int ripngd_instance_network_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct ripng *ripng;
- struct prefix p;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- yang_dnode_get_ipv6p(&p, dnode, NULL);
- apply_mask_ipv6((struct prefix_ipv6 *)&p);
-
- return ripng_enable_network_add(ripng, &p);
-}
-
-static int ripngd_instance_network_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct ripng *ripng;
- struct prefix p;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- yang_dnode_get_ipv6p(&p, dnode, NULL);
- apply_mask_ipv6((struct prefix_ipv6 *)&p);
-
- return ripng_enable_network_delete(ripng, &p);
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/interface
- */
-static int ripngd_instance_interface_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct ripng *ripng;
- const char *ifname;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- ifname = yang_dnode_get_string(dnode, NULL);
-
- return ripng_enable_if_add(ripng, ifname);
-}
-
-static int ripngd_instance_interface_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct ripng *ripng;
- const char *ifname;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- ifname = yang_dnode_get_string(dnode, NULL);
-
- return ripng_enable_if_delete(ripng, ifname);
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/offset-list
- */
-static int ripngd_instance_offset_list_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct ripng *ripng;
- const char *ifname;
- struct ripng_offset_list *offset;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- ifname = yang_dnode_get_string(dnode, "./interface");
-
- offset = ripng_offset_list_new(ripng, ifname);
- nb_running_set_entry(dnode, offset);
-
- return NB_OK;
-}
-
-static int ripngd_instance_offset_list_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- int direct;
- struct ripng_offset_list *offset;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- direct = yang_dnode_get_enum(dnode, "./direction");
-
- offset = nb_running_unset_entry(dnode);
- if (offset->direct[direct].alist_name) {
- free(offset->direct[direct].alist_name);
- offset->direct[direct].alist_name = NULL;
- }
- if (offset->direct[RIPNG_OFFSET_LIST_IN].alist_name == NULL
- && offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name == NULL)
- ripng_offset_list_del(offset);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/offset-list/access-list
- */
-static int
-ripngd_instance_offset_list_access_list_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- int direct;
- struct ripng_offset_list *offset;
- const char *alist_name;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- direct = yang_dnode_get_enum(dnode, "../direction");
- alist_name = yang_dnode_get_string(dnode, NULL);
-
- offset = nb_running_get_entry(dnode, NULL, true);
- if (offset->direct[direct].alist_name)
- free(offset->direct[direct].alist_name);
- offset->direct[direct].alist_name = strdup(alist_name);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/offset-list/metric
- */
-static int
-ripngd_instance_offset_list_metric_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- int direct;
- uint8_t metric;
- struct ripng_offset_list *offset;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- direct = yang_dnode_get_enum(dnode, "../direction");
- metric = yang_dnode_get_uint8(dnode, NULL);
-
- offset = nb_running_get_entry(dnode, NULL, true);
- offset->direct[direct].metric = metric;
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/passive-interface
- */
-static int
-ripngd_instance_passive_interface_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct ripng *ripng;
- const char *ifname;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- ifname = yang_dnode_get_string(dnode, NULL);
-
- return ripng_passive_interface_set(ripng, ifname);
-}
-
-static int
-ripngd_instance_passive_interface_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct ripng *ripng;
- const char *ifname;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- ifname = yang_dnode_get_string(dnode, NULL);
-
- return ripng_passive_interface_unset(ripng, ifname);
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/redistribute
- */
-static int ripngd_instance_redistribute_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct ripng *ripng;
- int type;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- type = yang_dnode_get_enum(dnode, "./protocol");
-
- ripng->redist[type].enabled = true;
-
- return NB_OK;
-}
-
-static int ripngd_instance_redistribute_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct ripng *ripng;
- int type;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- type = yang_dnode_get_enum(dnode, "./protocol");
-
- ripng->redist[type].enabled = false;
- if (ripng->redist[type].route_map.name) {
- free(ripng->redist[type].route_map.name);
- ripng->redist[type].route_map.name = NULL;
- ripng->redist[type].route_map.map = NULL;
- }
- ripng->redist[type].metric_config = false;
- ripng->redist[type].metric = 0;
-
- if (ripng->enabled)
- ripng_redistribute_conf_delete(ripng, type);
-
- return NB_OK;
-}
-
-static void
-ripngd_instance_redistribute_apply_finish(const struct lyd_node *dnode)
-{
- struct ripng *ripng;
- int type;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- type = yang_dnode_get_enum(dnode, "./protocol");
-
- if (ripng->enabled)
- ripng_redistribute_conf_update(ripng, type);
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/redistribute/route-map
- */
-static int
-ripngd_instance_redistribute_route_map_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct ripng *ripng;
- int type;
- const char *rmap_name;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- type = yang_dnode_get_enum(dnode, "../protocol");
- rmap_name = yang_dnode_get_string(dnode, NULL);
-
- if (ripng->redist[type].route_map.name)
- free(ripng->redist[type].route_map.name);
- ripng->redist[type].route_map.name = strdup(rmap_name);
- ripng->redist[type].route_map.map = route_map_lookup_by_name(rmap_name);
-
- return NB_OK;
-}
-
-static int
-ripngd_instance_redistribute_route_map_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct ripng *ripng;
- int type;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- type = yang_dnode_get_enum(dnode, "../protocol");
-
- free(ripng->redist[type].route_map.name);
- ripng->redist[type].route_map.name = NULL;
- ripng->redist[type].route_map.map = NULL;
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/redistribute/metric
- */
-static int
-ripngd_instance_redistribute_metric_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct ripng *ripng;
- int type;
- uint8_t metric;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- type = yang_dnode_get_enum(dnode, "../protocol");
- metric = yang_dnode_get_uint8(dnode, NULL);
-
- ripng->redist[type].metric_config = true;
- ripng->redist[type].metric = metric;
-
- return NB_OK;
-}
-
-static int
-ripngd_instance_redistribute_metric_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct ripng *ripng;
- int type;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- type = yang_dnode_get_enum(dnode, "../protocol");
-
- ripng->redist[type].metric_config = false;
- ripng->redist[type].metric = 0;
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/static-route
- */
-static int ripngd_instance_static_route_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct ripng *ripng;
- struct prefix_ipv6 p;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- yang_dnode_get_ipv6p(&p, dnode, NULL);
- apply_mask_ipv6(&p);
-
- ripng_redistribute_add(ripng, ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC, &p,
- 0, NULL, 0);
-
- return NB_OK;
-}
-
-static int ripngd_instance_static_route_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct ripng *ripng;
- struct prefix_ipv6 p;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- yang_dnode_get_ipv6p(&p, dnode, NULL);
- apply_mask_ipv6(&p);
-
- ripng_redistribute_delete(ripng, ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC,
- &p, 0);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/aggregate-address
- */
-static int
-ripngd_instance_aggregate_address_create(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct ripng *ripng;
- struct prefix_ipv6 p;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- yang_dnode_get_ipv6p(&p, dnode, NULL);
- apply_mask_ipv6(&p);
-
- ripng_aggregate_add(ripng, (struct prefix *)&p);
-
- return NB_OK;
-}
-
-static int
-ripngd_instance_aggregate_address_destroy(enum nb_event event,
- const struct lyd_node *dnode)
-{
- struct ripng *ripng;
- struct prefix_ipv6 p;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- yang_dnode_get_ipv6p(&p, dnode, NULL);
- apply_mask_ipv6(&p);
-
- ripng_aggregate_delete(ripng, (struct prefix *)&p);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/timers
- */
-static void ripngd_instance_timers_apply_finish(const struct lyd_node *dnode)
-{
- struct ripng *ripng;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
-
- /* Reset update timer thread. */
- ripng_event(ripng, RIPNG_UPDATE_EVENT, 0);
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/timers/flush-interval
- */
-static int
-ripngd_instance_timers_flush_interval_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct ripng *ripng;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- ripng->garbage_time = yang_dnode_get_uint16(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/timers/holddown-interval
- */
-static int
-ripngd_instance_timers_holddown_interval_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct ripng *ripng;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- ripng->timeout_time = yang_dnode_get_uint16(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/timers/update-interval
- */
-static int
-ripngd_instance_timers_update_interval_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct ripng *ripng;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ripng = nb_running_get_entry(dnode, NULL, true);
- ripng->update_time = yang_dnode_get_uint16(dnode, NULL);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor
- */
-static const void *
-ripngd_instance_state_neighbors_neighbor_get_next(const void *parent_list_entry,
- const void *list_entry)
-{
- const struct ripng *ripng = parent_list_entry;
- struct listnode *node;
-
- if (list_entry == NULL)
- node = listhead(ripng->peer_list);
- else
- node = listnextnode((struct listnode *)list_entry);
-
- return node;
-}
-
-static int
-ripngd_instance_state_neighbors_neighbor_get_keys(const void *list_entry,
- struct yang_list_keys *keys)
-{
- const struct listnode *node = list_entry;
- const struct ripng_peer *peer = listgetdata(node);
-
- keys->num = 1;
- (void)inet_ntop(AF_INET6, &peer->addr, keys->key[0],
- sizeof(keys->key[0]));
-
- return NB_OK;
-}
-
-static const void *ripngd_instance_state_neighbors_neighbor_lookup_entry(
- const void *parent_list_entry, const struct yang_list_keys *keys)
-{
- const struct ripng *ripng = parent_list_entry;
- struct in6_addr address;
- struct ripng_peer *peer;
- struct listnode *node;
-
- yang_str2ipv6(keys->key[0], &address);
-
- for (ALL_LIST_ELEMENTS_RO(ripng->peer_list, node, peer)) {
- if (IPV6_ADDR_SAME(&peer->addr, &address))
- return node;
- }
-
- return NULL;
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor/address
- */
-static struct yang_data *
-ripngd_instance_state_neighbors_neighbor_address_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct listnode *node = list_entry;
- const struct ripng_peer *peer = listgetdata(node);
-
- return yang_data_new_ipv6(xpath, &peer->addr);
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor/last-update
- */
-static struct yang_data *
-ripngd_instance_state_neighbors_neighbor_last_update_get_elem(
- const char *xpath, const void *list_entry)
-{
- /* TODO: yang:date-and-time is tricky */
- return NULL;
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor/bad-packets-rcvd
- */
-static struct yang_data *
-ripngd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct listnode *node = list_entry;
- const struct ripng_peer *peer = listgetdata(node);
-
- return yang_data_new_uint32(xpath, peer->recv_badpackets);
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor/bad-routes-rcvd
- */
-static struct yang_data *
-ripngd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem(
- const char *xpath, const void *list_entry)
-{
- const struct listnode *node = list_entry;
- const struct ripng_peer *peer = listgetdata(node);
-
- return yang_data_new_uint32(xpath, peer->recv_badroutes);
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/state/routes/route
- */
-static const void *
-ripngd_instance_state_routes_route_get_next(const void *parent_list_entry,
- const void *list_entry)
-{
- const struct ripng *ripng = parent_list_entry;
- struct agg_node *rn;
-
- if (list_entry == NULL)
- rn = agg_route_top(ripng->table);
- else
- rn = agg_route_next((struct agg_node *)list_entry);
- while (rn && rn->info == NULL)
- rn = agg_route_next(rn);
-
- return rn;
-}
-
-static int
-ripngd_instance_state_routes_route_get_keys(const void *list_entry,
- struct yang_list_keys *keys)
-{
- const struct agg_node *rn = list_entry;
-
- keys->num = 1;
- (void)prefix2str(&rn->p, keys->key[0], sizeof(keys->key[0]));
-
- return NB_OK;
-}
-
-static const void *ripngd_instance_state_routes_route_lookup_entry(
- const void *parent_list_entry, const struct yang_list_keys *keys)
-{
- const struct ripng *ripng = parent_list_entry;
- struct prefix prefix;
- struct agg_node *rn;
-
- yang_str2ipv6p(keys->key[0], &prefix);
-
- rn = agg_node_lookup(ripng->table, &prefix);
- if (!rn || !rn->info)
- return NULL;
-
- agg_unlock_node(rn);
-
- return rn;
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/state/routes/route/prefix
- */
-static struct yang_data *
-ripngd_instance_state_routes_route_prefix_get_elem(const char *xpath,
- const void *list_entry)
-{
- const struct agg_node *rn = list_entry;
- const struct ripng_info *rinfo = listnode_head(rn->info);
-
- return yang_data_new_ipv6p(xpath, &rinfo->rp->p);
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/state/routes/route/next-hop
- */
-static struct yang_data *
-ripngd_instance_state_routes_route_next_hop_get_elem(const char *xpath,
- const void *list_entry)
-{
- const struct agg_node *rn = list_entry;
- const struct ripng_info *rinfo = listnode_head(rn->info);
-
- return yang_data_new_ipv6(xpath, &rinfo->nexthop);
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/state/routes/route/interface
- */
-static struct yang_data *
-ripngd_instance_state_routes_route_interface_get_elem(const char *xpath,
- const void *list_entry)
-{
- const struct agg_node *rn = list_entry;
- const struct ripng_info *rinfo = listnode_head(rn->info);
- const struct ripng *ripng = ripng_info_get_instance(rinfo);
-
- return yang_data_new_string(
- xpath, ifindex2ifname(rinfo->ifindex, ripng->vrf->vrf_id));
-}
-
-/*
- * XPath: /frr-ripngd:ripngd/instance/state/routes/route/metric
- */
-static struct yang_data *
-ripngd_instance_state_routes_route_metric_get_elem(const char *xpath,
- const void *list_entry)
-{
- const struct agg_node *rn = list_entry;
- const struct ripng_info *rinfo = listnode_head(rn->info);
-
- return yang_data_new_uint8(xpath, rinfo->metric);
-}
-
-/*
- * XPath: /frr-ripngd:clear-ripng-route
- */
-static void clear_ripng_route(struct ripng *ripng)
-{
- struct agg_node *rp;
-
- if (IS_RIPNG_DEBUG_EVENT)
- zlog_debug("Clearing all RIPng routes (VRF %s)",
- ripng->vrf_name);
-
- /* Clear received RIPng routes */
- for (rp = agg_route_top(ripng->table); rp; rp = agg_route_next(rp)) {
- struct list *list;
- struct listnode *listnode;
- struct ripng_info *rinfo;
-
- list = rp->info;
- if (list == NULL)
- continue;
-
- for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) {
- if (!ripng_route_rte(rinfo))
- continue;
-
- if (CHECK_FLAG(rinfo->flags, RIPNG_RTF_FIB))
- ripng_zebra_ipv6_delete(ripng, rp);
- break;
- }
-
- if (rinfo) {
- RIPNG_TIMER_OFF(rinfo->t_timeout);
- RIPNG_TIMER_OFF(rinfo->t_garbage_collect);
- listnode_delete(list, rinfo);
- ripng_info_free(rinfo);
- }
-
- if (list_isempty(list)) {
- list_delete(&list);
- rp->info = NULL;
- agg_unlock_node(rp);
- }
- }
-}
-
-static int clear_ripng_route_rpc(const char *xpath, const struct list *input,
- struct list *output)
-{
- struct ripng *ripng;
- struct yang_data *yang_vrf;
-
- yang_vrf = yang_data_list_find(input, "%s/%s", xpath, "input/vrf");
- if (yang_vrf) {
- ripng = ripng_lookup_by_vrf_name(yang_vrf->value);
- if (ripng)
- clear_ripng_route(ripng);
- } else {
- struct vrf *vrf;
-
- RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
- ripng = vrf->info;
- if (!ripng)
- continue;
-
- clear_ripng_route(ripng);
- }
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/frr-ripngd:ripng/split-horizon
- */
-static int
-lib_interface_ripng_split_horizon_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
-{
- struct interface *ifp;
- struct ripng_interface *ri;
-
- if (event != NB_EV_APPLY)
- return NB_OK;
-
- ifp = nb_running_get_entry(dnode, NULL, true);
- ri = ifp->info;
- ri->split_horizon = yang_dnode_get_enum(dnode, NULL);
-
- return NB_OK;
-}
-
-/* clang-format off */
-const struct frr_yang_module_info frr_ripngd_info = {
- .name = "frr-ripngd",
- .nodes = {
- {
- .xpath = "/frr-ripngd:ripngd/instance",
- .cbs = {
- .cli_show = cli_show_router_ripng,
- .create = ripngd_instance_create,
- .destroy = ripngd_instance_destroy,
- .get_keys = ripngd_instance_get_keys,
- .get_next = ripngd_instance_get_next,
- .lookup_entry = ripngd_instance_lookup_entry,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/allow-ecmp",
- .cbs = {
- .cli_show = cli_show_ripng_allow_ecmp,
- .modify = ripngd_instance_allow_ecmp_modify,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/default-information-originate",
- .cbs = {
- .cli_show = cli_show_ripng_default_information_originate,
- .modify = ripngd_instance_default_information_originate_modify,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/default-metric",
- .cbs = {
- .cli_show = cli_show_ripng_default_metric,
- .modify = ripngd_instance_default_metric_modify,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/network",
- .cbs = {
- .cli_show = cli_show_ripng_network_prefix,
- .create = ripngd_instance_network_create,
- .destroy = ripngd_instance_network_destroy,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/interface",
- .cbs = {
- .cli_show = cli_show_ripng_network_interface,
- .create = ripngd_instance_interface_create,
- .destroy = ripngd_instance_interface_destroy,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/offset-list",
- .cbs = {
- .cli_show = cli_show_ripng_offset_list,
- .create = ripngd_instance_offset_list_create,
- .destroy = ripngd_instance_offset_list_destroy,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/offset-list/access-list",
- .cbs = {
- .modify = ripngd_instance_offset_list_access_list_modify,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/offset-list/metric",
- .cbs = {
- .modify = ripngd_instance_offset_list_metric_modify,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/passive-interface",
- .cbs = {
- .cli_show = cli_show_ripng_passive_interface,
- .create = ripngd_instance_passive_interface_create,
- .destroy = ripngd_instance_passive_interface_destroy,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/redistribute",
- .cbs = {
- .apply_finish = ripngd_instance_redistribute_apply_finish,
- .cli_show = cli_show_ripng_redistribute,
- .create = ripngd_instance_redistribute_create,
- .destroy = ripngd_instance_redistribute_destroy,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/redistribute/route-map",
- .cbs = {
- .destroy = ripngd_instance_redistribute_route_map_destroy,
- .modify = ripngd_instance_redistribute_route_map_modify,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/redistribute/metric",
- .cbs = {
- .destroy = ripngd_instance_redistribute_metric_destroy,
- .modify = ripngd_instance_redistribute_metric_modify,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/static-route",
- .cbs = {
- .cli_show = cli_show_ripng_route,
- .create = ripngd_instance_static_route_create,
- .destroy = ripngd_instance_static_route_destroy,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/aggregate-address",
- .cbs = {
- .cli_show = cli_show_ripng_aggregate_address,
- .create = ripngd_instance_aggregate_address_create,
- .destroy = ripngd_instance_aggregate_address_destroy,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/timers",
- .cbs = {
- .apply_finish = ripngd_instance_timers_apply_finish,
- .cli_show = cli_show_ripng_timers,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/timers/flush-interval",
- .cbs = {
- .modify = ripngd_instance_timers_flush_interval_modify,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/timers/holddown-interval",
- .cbs = {
- .modify = ripngd_instance_timers_holddown_interval_modify,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/timers/update-interval",
- .cbs = {
- .modify = ripngd_instance_timers_update_interval_modify,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor",
- .cbs = {
- .get_keys = ripngd_instance_state_neighbors_neighbor_get_keys,
- .get_next = ripngd_instance_state_neighbors_neighbor_get_next,
- .lookup_entry = ripngd_instance_state_neighbors_neighbor_lookup_entry,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor/address",
- .cbs = {
- .get_elem = ripngd_instance_state_neighbors_neighbor_address_get_elem,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor/last-update",
- .cbs = {
- .get_elem = ripngd_instance_state_neighbors_neighbor_last_update_get_elem,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor/bad-packets-rcvd",
- .cbs = {
- .get_elem = ripngd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor/bad-routes-rcvd",
- .cbs = {
- .get_elem = ripngd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/state/routes/route",
- .cbs = {
- .get_keys = ripngd_instance_state_routes_route_get_keys,
- .get_next = ripngd_instance_state_routes_route_get_next,
- .lookup_entry = ripngd_instance_state_routes_route_lookup_entry,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/state/routes/route/prefix",
- .cbs = {
- .get_elem = ripngd_instance_state_routes_route_prefix_get_elem,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/state/routes/route/next-hop",
- .cbs = {
- .get_elem = ripngd_instance_state_routes_route_next_hop_get_elem,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/state/routes/route/interface",
- .cbs = {
- .get_elem = ripngd_instance_state_routes_route_interface_get_elem,
- },
- },
- {
- .xpath = "/frr-ripngd:ripngd/instance/state/routes/route/metric",
- .cbs = {
- .get_elem = ripngd_instance_state_routes_route_metric_get_elem,
- },
- },
- {
- .xpath = "/frr-ripngd:clear-ripng-route",
- .cbs = {
- .rpc = clear_ripng_route_rpc,
- },
- },
- {
- .xpath = "/frr-interface:lib/interface/frr-ripngd:ripng/split-horizon",
- .cbs = {
- .cli_show = cli_show_ipv6_ripng_split_horizon,
- .modify = lib_interface_ripng_split_horizon_modify,
- },
- },
- {
- .xpath = NULL,
- },
- }
-};
diff --git a/ripngd/ripngd.h b/ripngd/ripngd.h
index 5a0d350a84..70508d5cb0 100644
--- a/ripngd/ripngd.h
+++ b/ripngd/ripngd.h
@@ -494,9 +494,6 @@ extern struct ripng_info *ripng_ecmp_delete(struct ripng *ripng,
extern void ripng_vrf_init(void);
extern void ripng_vrf_terminate(void);
-
-/* Northbound. */
extern void ripng_cli_init(void);
-extern const struct frr_yang_module_info frr_ripngd_info;
#endif /* _ZEBRA_RIPNG_RIPNGD_H */
diff --git a/ripngd/subdir.am b/ripngd/subdir.am
index eac0d67313..07d0cb892c 100644
--- a/ripngd/subdir.am
+++ b/ripngd/subdir.am
@@ -19,7 +19,10 @@ ripngd_libripng_a_SOURCES = \
ripngd/ripng_interface.c \
ripngd/ripng_nexthop.c \
ripngd/ripng_offset.c \
- ripngd/ripng_northbound.c \
+ ripngd/ripng_nb.c \
+ ripngd/ripng_nb_config.c \
+ ripngd/ripng_nb_rpcs.c \
+ ripngd/ripng_nb_state.c \
ripngd/ripng_peer.c \
ripngd/ripng_route.c \
ripngd/ripng_routemap.c \
@@ -31,8 +34,8 @@ ripngd/ripng_cli_clippy.c: $(CLIPPY_DEPS)
ripngd/ripng_cli.$(OBJEXT): ripngd/ripng_cli_clippy.c
noinst_HEADERS += \
- ripngd/ripng_cli.h \
ripngd/ripng_debug.h \
+ ripngd/ripng_nb.h \
ripngd/ripng_nexthop.h \
ripngd/ripng_route.h \
ripngd/ripngd.h \
diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c
index da2aa2f539..cd577e9051 100644
--- a/sharpd/sharp_zebra.c
+++ b/sharpd/sharp_zebra.c
@@ -73,7 +73,7 @@ static int interface_address_delete(ZAPI_CALLBACK_ARGS)
if (!c)
return 0;
- connected_free(c);
+ connected_free(&c);
return 0;
}
diff --git a/staticd/static_routes.c b/staticd/static_routes.c
index a8a5ca523a..e8d6a4289b 100644
--- a/staticd/static_routes.c
+++ b/staticd/static_routes.c
@@ -310,10 +310,14 @@ static void static_ifindex_update_af(struct interface *ifp, bool up, afi_t afi,
if (up) {
if (strcmp(si->ifname, ifp->name))
continue;
+ if (si->nh_vrf_id != ifp->vrf_id)
+ continue;
si->ifindex = ifp->ifindex;
} else {
if (si->ifindex != ifp->ifindex)
continue;
+ if (si->nh_vrf_id != ifp->vrf_id)
+ continue;
si->ifindex = IFINDEX_INTERNAL;
}
diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c
index 976f892efb..a474613b4d 100644
--- a/staticd/static_zebra.c
+++ b/staticd/static_zebra.c
@@ -79,7 +79,7 @@ static int interface_address_delete(ZAPI_CALLBACK_ARGS)
if (!c)
return 0;
- connected_free(c);
+ connected_free(&c);
return 0;
}
@@ -260,7 +260,7 @@ static void static_nht_hash_free(void *data)
{
struct static_nht_data *nhtd = data;
- prefix_free(nhtd->nh);
+ prefix_free(&nhtd->nh);
XFREE(MTYPE_TMP, nhtd);
}
diff --git a/tests/topotests/Dockerfile b/tests/topotests/Dockerfile
index fc6d6df530..cdd0ae2f6e 100644
--- a/tests/topotests/Dockerfile
+++ b/tests/topotests/Dockerfile
@@ -6,9 +6,11 @@ RUN export DEBIAN_FRONTEND=noninteractive \
autoconf \
binutils \
bison \
+ ca-certificates \
flex \
gdb \
git \
+ gpg \
install-info \
iputils-ping \
iproute2 \
@@ -38,17 +40,15 @@ RUN export DEBIAN_FRONTEND=noninteractive \
&& pip install \
exabgp==3.4.17 \
ipaddr \
- pytest
+ pytest \
+ && rm -rf /var/lib/apt/lists/*
-RUN cd /tmp \
- && wget -q https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/Debian-AMD64-Packages/libyang-dev_0.16.105-1_amd64.deb \
- -O libyang-dev.deb \
- && wget -q https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/Debian-AMD64-Packages/libyang0.16_0.16.105-1_amd64.deb \
- -O libyang.deb \
- && echo "34bef017e527a590020185f05dc39203bdf1c86223e0d990839623ec629d8598 libyang.deb" | sha256sum -c - \
- && echo "fe9cc6e3b173ca56ef49428c281e96bf76c0f910aa75cf85098076411484e8f4 libyang-dev.deb" | sha256sum -c - \
- && dpkg -i libyang*.deb \
- && rm libyang*.deb
+RUN export DEBIAN_FRONTEND=noninteractive \
+ && apt-key adv --keyserver keyserver.ubuntu.com --recv-key 5418F291D0D4A1AA \
+ && echo "deb https://deb.frrouting.org/frr bionic frr-stable" > /etc/apt/sources.list.d/frr.list \
+ && apt-get update \
+ && apt-get install -y libyang-dev \
+ && rm -rf /var/lib/apt/lists/*
RUN groupadd -r -g 92 frr \
&& groupadd -r -g 85 frrvty \
diff --git a/tests/topotests/bfd-topo2/r1/ipv6_routes.json b/tests/topotests/bfd-topo2/r1/ipv6_routes.json
index d09439a8a6..0fd03b516d 100644
--- a/tests/topotests/bfd-topo2/r1/ipv6_routes.json
+++ b/tests/topotests/bfd-topo2/r1/ipv6_routes.json
@@ -33,7 +33,6 @@
{
"interfaceName": "r1-eth0",
"interfaceIndex": 2,
- "flags": 1,
"active": true,
"afi": "ipv6"
}
diff --git a/tests/topotests/bfd-topo2/r2/ipv4_routes.json b/tests/topotests/bfd-topo2/r2/ipv4_routes.json
index 3c41e13434..69a5f1a5bc 100644
--- a/tests/topotests/bfd-topo2/r2/ipv4_routes.json
+++ b/tests/topotests/bfd-topo2/r2/ipv4_routes.json
@@ -11,7 +11,6 @@
{
"active": true,
"directlyConnected": true,
- "flags": 1,
"interfaceIndex": 3,
"interfaceName": "r2-eth1"
}
diff --git a/tests/topotests/bfd-topo2/r2/ipv6_routes.json b/tests/topotests/bfd-topo2/r2/ipv6_routes.json
index bb45bbae52..66abade380 100644
--- a/tests/topotests/bfd-topo2/r2/ipv6_routes.json
+++ b/tests/topotests/bfd-topo2/r2/ipv6_routes.json
@@ -11,7 +11,6 @@
{
"active": true,
"directlyConnected": true,
- "flags": 1,
"interfaceIndex": 4,
"interfaceName": "r2-eth2"
}
diff --git a/tests/topotests/bfd-topo2/r3/ipv4_routes.json b/tests/topotests/bfd-topo2/r3/ipv4_routes.json
index cbf116e687..d4a0812ae1 100644
--- a/tests/topotests/bfd-topo2/r3/ipv4_routes.json
+++ b/tests/topotests/bfd-topo2/r3/ipv4_routes.json
@@ -11,7 +11,6 @@
{
"active": true,
"directlyConnected": true,
- "flags": 1,
"interfaceIndex": 2,
"interfaceName": "r3-eth0"
}
diff --git a/tests/topotests/bfd-topo2/r4/ipv6_routes.json b/tests/topotests/bfd-topo2/r4/ipv6_routes.json
index a22c90cbba..af8272c4af 100644
--- a/tests/topotests/bfd-topo2/r4/ipv6_routes.json
+++ b/tests/topotests/bfd-topo2/r4/ipv6_routes.json
@@ -11,7 +11,6 @@
{
"active": true,
"directlyConnected": true,
- "flags": 1,
"interfaceIndex": 2,
"interfaceName": "r4-eth0"
}
diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json b/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json
index d0378b5649..acf5c8b276 100644
--- a/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json
+++ b/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json
@@ -10,7 +10,6 @@
{
"interfaceName": "r1-eth0",
"interfaceIndex": 2,
- "flags": 1,
"active": true,
"afi": "ipv6"
}
diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json
index 1ca62094bd..e5aff94bdd 100644
--- a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json
+++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json
@@ -10,7 +10,6 @@
"internalFlags": 0,
"nexthops": [
{
- "flags": 1,
"afi": "ipv6",
"interfaceIndex": 2,
"interfaceName": "r1-eth0",
diff --git a/tests/topotests/ldp-vpls-topo1/r1/zebra.conf b/tests/topotests/ldp-vpls-topo1/r1/zebra.conf
index edfa1780a9..ea047355ad 100644
--- a/tests/topotests/ldp-vpls-topo1/r1/zebra.conf
+++ b/tests/topotests/ldp-vpls-topo1/r1/zebra.conf
@@ -14,17 +14,14 @@ interface lo
!
interface r1-eth0
description to s1
- no link-detect
!
interface r1-eth1
description to s4
ip address 10.0.1.1/24
- no link-detect
!
interface r1-eth2
description to s5
ip address 10.0.2.1/24
- no link-detect
!
ip forwarding
!
diff --git a/tests/topotests/ldp-vpls-topo1/r2/zebra.conf b/tests/topotests/ldp-vpls-topo1/r2/zebra.conf
index 6b95efdce8..c244442876 100644
--- a/tests/topotests/ldp-vpls-topo1/r2/zebra.conf
+++ b/tests/topotests/ldp-vpls-topo1/r2/zebra.conf
@@ -13,17 +13,14 @@ interface lo
!
interface r2-eth0
description to s2
- no link-detect
!
interface r2-eth1
description to s4
ip address 10.0.1.2/24
- no link-detect
!
interface r2-eth2
description to s6
ip address 10.0.3.2/24
- no link-detect
!
ip forwarding
!
diff --git a/tests/topotests/ldp-vpls-topo1/r3/zebra.conf b/tests/topotests/ldp-vpls-topo1/r3/zebra.conf
index 85ec68ff32..6b1eaa2ca0 100644
--- a/tests/topotests/ldp-vpls-topo1/r3/zebra.conf
+++ b/tests/topotests/ldp-vpls-topo1/r3/zebra.conf
@@ -13,17 +13,14 @@ interface lo
!
interface r3-eth0
description to s3
- no link-detect
!
interface r3-eth1
description to s5
ip address 10.0.2.3/24
- no link-detect
!
interface r3-eth2
description to s6
ip address 10.0.3.3/24
- no link-detect
!
ip forwarding
!
diff --git a/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py b/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py
index ce651c50cd..0fae64402a 100755
--- a/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py
+++ b/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py
@@ -257,6 +257,7 @@ def test_ldp_pseudowires_after_link_down():
# Shut down r1-r2 link */
tgen = get_topogen()
tgen.gears['r1'].peer_link_enable('r1-eth1', False)
+ topotest.sleep(5, "Waiting for the network to reconverge")
# check if the pseudowire is still up (using an alternate path for nexthop resolution)
for rname in ['r1', 'r2', 'r3']:
diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py
index d7145c3be0..6859f5a076 100644
--- a/tests/topotests/lib/topogen.py
+++ b/tests/topotests/lib/topogen.py
@@ -61,6 +61,7 @@ from mininet.cli import CLI
from lib import topotest
from lib.topolog import logger, logger_config
+from lib.topotest import set_sysctl
CWD = os.path.dirname(os.path.realpath(__file__))
@@ -676,6 +677,10 @@ class TopoRouter(TopoGear):
if result != '':
self.tgen.set_error(result)
+ else:
+ # Enable MPLS processing on all interfaces.
+ for interface in self.links.keys():
+ set_sysctl(nrouter, 'net.mpls.conf.{}.input'.format(interface), 1)
return result
diff --git a/tools/gen_northbound_callbacks.c b/tools/gen_northbound_callbacks.c
index 14f648e8da..cbdf01e7b8 100644
--- a/tools/gen_northbound_callbacks.c
+++ b/tools/gen_northbound_callbacks.c
@@ -26,10 +26,12 @@
#include "yang.h"
#include "northbound.h"
+static bool static_cbs;
+
static void __attribute__((noreturn)) usage(int status)
{
extern const char *__progname;
- fprintf(stderr, "usage: %s [-h] [-p path] MODULE\n", __progname);
+ fprintf(stderr, "usage: %s [-h] [-s] [-p path] MODULE\n", __progname);
exit(status);
}
@@ -153,10 +155,46 @@ static void generate_callback_name(struct lys_node *snode,
replace_hyphens_by_underscores(buffer);
}
+static void generate_prototype(const struct nb_callback_info *ncinfo,
+ const char *cb_name)
+{
+ printf("%s%s(%s);\n", ncinfo->return_type, cb_name, ncinfo->arguments);
+}
+
+static int generate_prototypes(const struct lys_node *snode, void *arg)
+{
+ switch (snode->nodetype) {
+ case LYS_CONTAINER:
+ case LYS_LEAF:
+ case LYS_LEAFLIST:
+ case LYS_LIST:
+ case LYS_NOTIF:
+ case LYS_RPC:
+ break;
+ default:
+ return YANG_ITER_CONTINUE;
+ }
+
+ for (struct nb_callback_info *cb = &nb_callbacks[0];
+ cb->operation != -1; cb++) {
+ char cb_name[BUFSIZ];
+
+ if (cb->optional
+ || !nb_operation_is_valid(cb->operation, snode))
+ continue;
+
+ generate_callback_name((struct lys_node *)snode, cb->operation,
+ cb_name, sizeof(cb_name));
+ generate_prototype(cb, cb_name);
+ }
+
+ return YANG_ITER_CONTINUE;
+}
+
static void generate_callback(const struct nb_callback_info *ncinfo,
const char *cb_name)
{
- printf("static %s%s(%s)\n{\n",
+ printf("%s%s%s(%s)\n{\n", static_cbs ? "static " : "",
ncinfo->return_type, cb_name, ncinfo->arguments);
switch (ncinfo->operation) {
@@ -287,7 +325,7 @@ int main(int argc, char *argv[])
struct stat st;
int opt;
- while ((opt = getopt(argc, argv, "hp:")) != -1) {
+ while ((opt = getopt(argc, argv, "hp:s")) != -1) {
switch (opt) {
case 'h':
usage(EXIT_SUCCESS);
@@ -307,6 +345,9 @@ int main(int argc, char *argv[])
search_path = optarg;
break;
+ case 's':
+ static_cbs = true;
+ break;
default:
usage(EXIT_FAILURE);
/* NOTREACHED */
@@ -332,6 +373,14 @@ int main(int argc, char *argv[])
/* Create a nb_node for all YANG schema nodes. */
nb_nodes_create();
+ /* Generate callback prototypes. */
+ if (!static_cbs) {
+ printf("/* prototypes */\n");
+ yang_snodes_iterate_module(module->info, generate_prototypes, 0,
+ NULL);
+ printf("\n");
+ }
+
/* Generate callback functions. */
yang_snodes_iterate_module(module->info, generate_callbacks, 0, NULL);
diff --git a/tools/tarsource.sh b/tools/tarsource.sh
index eee2a9739b..4843fe88b0 100755
--- a/tools/tarsource.sh
+++ b/tools/tarsource.sh
@@ -301,6 +301,11 @@ if $debian; then
--format='3.0 (custom)' --target-format='3.0 (quilt)' \
-b . frr_${PACKAGE_VERSION}.orig.tar.$zip frr_${DEBVER}.debian.tar.$zip
+ dpkg-genchanges -sa -S > ../frr_${DEBVER}_source.changes
+
+ test -n "$keyid" && debsign ../frr_${DEBVER}_source.changes -k"$keyid"
+
+ mv ../frr_${DEBVER}_source.changes "$outdir" || true
mv ../frr_${DEBVER}.dsc "$outdir" || true
mv ../frr_${DEBVER}.debian.tar.$zip "$outdir" || true
if test -h ../frr_${PACKAGE_VERSION}.orig.tar.$zip; then
@@ -309,12 +314,12 @@ if $debian; then
ln -s frr-${PACKAGE_VERSION}.tar.$zip "$outdir/frr_${PACKAGE_VERSION}.orig.tar.$zip" || true
cd "$outdir"
- test -n "$keyid" && debsign -k "$keyid" "frr_${DEBVER}.dsc"
lsfiles="$lsfiles \
frr_${DEBVER}.dsc \
frr_${DEBVER}.debian.tar.$zip \
- frr_${PACKAGE_VERSION}.orig.tar.$zip"
+ frr_${PACKAGE_VERSION}.orig.tar.$zip \
+ frr_${DEBVER}_source.changes"
fi
cd "$outdir"
diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c
index 819a06e99a..eed0e8a31a 100644
--- a/vrrpd/vrrp.c
+++ b/vrrpd/vrrp.c
@@ -289,27 +289,30 @@ void vrrp_check_start(struct vrrp_vrouter *vr)
r = vr->v4;
/* Must not already be started */
start = r->fsm.state == VRRP_STATE_INITIALIZE;
+ whynot = (!start && !whynot) ? "Already running" : whynot;
/* Must have a parent interface */
start = start && (vr->ifp != NULL);
- whynot = (!start && !whynot) ? "No base interface" : NULL;
+ whynot = (!start && !whynot) ? "No base interface" : whynot;
#if 0
/* Parent interface must be up */
start = start && if_is_operative(vr->ifp);
+ start = (!start && !whynot) ? "Base interface inoperative" : whynot;
#endif
/* Parent interface must have at least one v4 */
- start = start && vr->ifp->connected->count > 1;
- whynot = (!start && !whynot) ? "No primary IPv4 address" : NULL;
+ start = start && connected_count_by_family(vr->ifp, AF_INET) > 0;
+ whynot = (!start && !whynot) ? "No primary IPv4 address" : whynot;
/* Must have a macvlan interface */
start = start && (r->mvl_ifp != NULL);
- whynot = (!start && !whynot) ? "No VRRP interface" : NULL;
+ whynot = (!start && !whynot) ? "No VRRP interface" : whynot;
#if 0
/* Macvlan interface must be admin up */
start = start && CHECK_FLAG(r->mvl_ifp->flags, IFF_UP);
+ start = (!start && !whynot) ? "Macvlan device admin down" : whynot;
#endif
/* Must have at least one VIP configured */
start = start && r->addrs->count > 0;
- whynot =
- (!start && !whynot) ? "No Virtual IP address configured" : NULL;
+ whynot = (!start && !whynot) ? "No Virtual IP address configured"
+ : whynot;
if (start)
vrrp_event(r, VRRP_EVENT_STARTUP);
else if (whynot)
@@ -317,39 +320,44 @@ void vrrp_check_start(struct vrrp_vrouter *vr)
"Refusing to start Virtual Router: %s",
vr->vrid, family2str(r->family), whynot);
+ whynot = NULL;
+
r = vr->v6;
/* Must not already be started */
start = r->fsm.state == VRRP_STATE_INITIALIZE;
+ whynot = (!start && !whynot) ? "Already running" : whynot;
/* Must not be v2 */
start = start && vr->version != 2;
- whynot = (!start && !whynot) ? "VRRPv2 does not support v6" : NULL;
+ whynot = (!start && !whynot) ? "VRRPv2 does not support v6" : whynot;
/* Must have a parent interface */
start = start && (vr->ifp != NULL);
- whynot = (!start && !whynot) ? "No base interface" : NULL;
+ whynot = (!start && !whynot) ? "No base interface" : whynot;
#if 0
/* Parent interface must be up */
start = start && if_is_operative(vr->ifp);
+ start = (!start && !whynot) ? "Base interface inoperative" : whynot;
#endif
/* Must have a macvlan interface */
start = start && (r->mvl_ifp != NULL);
- whynot = (!start && !whynot) ? "No VRRP interface" : NULL;
+ whynot = (!start && !whynot) ? "No VRRP interface" : whynot;
#if 0
/* Macvlan interface must be admin up */
start = start && CHECK_FLAG(r->mvl_ifp->flags, IFF_UP);
+ start = (!start && !whynot) ? "Macvlan device admin down" : whynot;
/* Macvlan interface must have a link local */
start = start && connected_get_linklocal(r->mvl_ifp);
whynot =
- (!start && !whynot) ? "No link local address configured" : NULL;
+ (!start && !whynot) ? "No link local address configured" : whynot;
/* Macvlan interface must have a v6 IP besides the link local */
- start = start && (r->mvl_ifp->connected->count >= 2);
+ start = start && (connected_count_by_family(r->mvl_ifp, AF_INET6) > 1);
whynot = (!start && !whynot)
- ? "No Virtual IP configured on macvlan device"
- : NULL;
+ ? "No Virtual IPv6 address configured on macvlan device"
+ : whynot;
#endif
/* Must have at least one VIP configured */
start = start && r->addrs->count > 0;
whynot =
- (!start && !whynot) ? "No Virtual IP address configured" : NULL;
+ (!start && !whynot) ? "No Virtual IP address configured" : whynot;
if (start)
vrrp_event(r, VRRP_EVENT_STARTUP);
else if (whynot)
diff --git a/zebra/connected.c b/zebra/connected.c
index 87cf8c8f20..0ff474d787 100644
--- a/zebra/connected.c
+++ b/zebra/connected.c
@@ -65,7 +65,7 @@ static void connected_withdraw(struct connected *ifc)
if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) {
listnode_delete(ifc->ifp->connected, ifc);
- connected_free(ifc);
+ connected_free(&ifc);
}
}
@@ -177,7 +177,7 @@ static void connected_update(struct interface *ifp, struct connected *ifc)
*/
if (connected_same(current, ifc)) {
/* nothing to do */
- connected_free(ifc);
+ connected_free(&ifc);
return;
}
@@ -199,7 +199,7 @@ static void connected_update(struct interface *ifp, struct connected *ifc)
void connected_up(struct interface *ifp, struct connected *ifc)
{
afi_t afi;
- struct prefix p;
+ struct prefix p = {0};
struct nexthop nh = {
.type = NEXTHOP_TYPE_IFINDEX,
.ifindex = ifp->ifindex,
@@ -251,10 +251,10 @@ void connected_up(struct interface *ifp, struct connected *ifc)
metric = (ifc->metric < (uint32_t)METRIC_MAX) ?
ifc->metric : ifp->metric;
rib_add(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT,
- 0, 0, &p, NULL, &nh, zvrf->table_id, metric, 0, 0, 0);
+ 0, 0, &p, NULL, &nh, 0, zvrf->table_id, metric, 0, 0, 0);
rib_add(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT,
- 0, 0, &p, NULL, &nh, zvrf->table_id, metric, 0, 0, 0);
+ 0, 0, &p, NULL, &nh, 0, zvrf->table_id, metric, 0, 0, 0);
/* Schedule LSP forwarding entries for processing, if appropriate. */
if (zvrf->vrf->vrf_id == VRF_DEFAULT) {
@@ -393,11 +393,11 @@ void connected_down(struct interface *ifp, struct connected *ifc)
* Same logic as for connected_up(): push the changes into the
* head.
*/
- rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT,
- 0, 0, &p, NULL, &nh, zvrf->table_id, 0, 0, false);
+ rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, 0,
+ 0, &p, NULL, &nh, 0, zvrf->table_id, 0, 0, false);
rib_delete(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT,
- 0, 0, &p, NULL, &nh, zvrf->table_id, 0, 0, false);
+ 0, 0, &p, NULL, &nh, 0, zvrf->table_id, 0, 0, false);
/* Schedule LSP forwarding entries for processing, if appropriate. */
if (zvrf->vrf->vrf_id == VRF_DEFAULT) {
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index d42f68cbe8..c09007bcb1 100644
--- a/zebra/if_netlink.c
+++ b/zebra/if_netlink.c
@@ -66,6 +66,7 @@
#include "zebra/zebra_ptm.h"
#include "zebra/zebra_mpls.h"
#include "zebra/kernel_netlink.h"
+#include "zebra/rt_netlink.h"
#include "zebra/if_netlink.h"
#include "zebra/zebra_errors.h"
#include "zebra/zebra_vxlan.h"
@@ -807,6 +808,23 @@ int interface_lookup_netlink(struct zebra_ns *zns)
/* fixup linkages */
zebra_if_update_all_links();
+ return 0;
+}
+
+/**
+ * interface_addr_lookup_netlink() - Look up interface addresses
+ *
+ * @zns: Zebra netlink socket
+ * Return: Result status
+ */
+static int interface_addr_lookup_netlink(struct zebra_ns *zns)
+{
+ int ret;
+ struct zebra_dplane_info dp_info;
+ struct nlsock *netlink_cmd = &zns->netlink_cmd;
+
+ /* Capture key info from ns struct */
+ zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/);
/* Get IPv4 address of the interfaces. */
ret = netlink_request_intf_addr(netlink_cmd, AF_INET, RTM_GETADDR, 0);
@@ -1460,6 +1478,13 @@ int netlink_protodown(struct interface *ifp, bool down)
void interface_list(struct zebra_ns *zns)
{
interface_lookup_netlink(zns);
+ /* We add routes for interface address,
+ * so we need to get the nexthop info
+ * from the kernel before we can do that
+ */
+ netlink_nexthop_read(zns);
+
+ interface_addr_lookup_netlink(zns);
}
#endif /* GNU_LINUX */
diff --git a/zebra/interface.c b/zebra/interface.c
index ef03cf87f6..eea80652e5 100644
--- a/zebra/interface.c
+++ b/zebra/interface.c
@@ -107,6 +107,17 @@ static void zebra_if_node_destroy(route_table_delegate_t *delegate,
route_node_destroy(delegate, table, node);
}
+static void zebra_if_nhg_dependents_free(struct zebra_if *zebra_if)
+{
+ nhg_connected_tree_free(&zebra_if->nhg_dependents);
+}
+
+static void zebra_if_nhg_dependents_init(struct zebra_if *zebra_if)
+{
+ nhg_connected_tree_init(&zebra_if->nhg_dependents);
+}
+
+
route_table_delegate_t zebra_if_table_delegate = {
.create_node = route_node_create,
.destroy_node = zebra_if_node_destroy};
@@ -120,6 +131,9 @@ static int if_zebra_new_hook(struct interface *ifp)
zebra_if->multicast = IF_ZEBRA_MULTICAST_UNSPEC;
zebra_if->shutdown = IF_ZEBRA_SHUTDOWN_OFF;
+
+ zebra_if_nhg_dependents_init(zebra_if);
+
zebra_ptm_if_init(zebra_if);
ifp->ptm_enable = zebra_ptm_get_enable_state();
@@ -175,6 +189,34 @@ static int if_zebra_new_hook(struct interface *ifp)
return 0;
}
+static void if_nhg_dependents_check_valid(struct nhg_hash_entry *nhe)
+{
+ zebra_nhg_check_valid(nhe);
+ if (!CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_VALID))
+ /* Assuming uninstalled as well here */
+ UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
+}
+
+static void if_down_nhg_dependents(const struct interface *ifp)
+{
+ struct nhg_connected *rb_node_dep = NULL;
+ struct zebra_if *zif = (struct zebra_if *)ifp->info;
+
+ frr_each(nhg_connected_tree, &zif->nhg_dependents, rb_node_dep)
+ if_nhg_dependents_check_valid(rb_node_dep->nhe);
+}
+
+static void if_nhg_dependents_release(const struct interface *ifp)
+{
+ struct nhg_connected *rb_node_dep = NULL;
+ struct zebra_if *zif = (struct zebra_if *)ifp->info;
+
+ frr_each(nhg_connected_tree, &zif->nhg_dependents, rb_node_dep) {
+ rb_node_dep->nhe->ifp = NULL; /* Null it out */
+ if_nhg_dependents_check_valid(rb_node_dep->nhe);
+ }
+}
+
/* Called when interface is deleted. */
static int if_zebra_delete_hook(struct interface *ifp)
{
@@ -196,7 +238,11 @@ static int if_zebra_delete_hook(struct interface *ifp)
list_delete(&rtadv->AdvDNSSLList);
#endif /* HAVE_RTADV */
+ if_nhg_dependents_release(ifp);
+ zebra_if_nhg_dependents_free(zebra_if);
+
XFREE(MTYPE_TMP, zebra_if->desc);
+
THREAD_OFF(zebra_if->speed_update);
XFREE(MTYPE_ZINFO, zebra_if);
@@ -692,7 +738,7 @@ static void if_delete_connected(struct interface *ifp)
ZEBRA_IFC_CONFIGURED)) {
listnode_delete(ifp->connected,
ifc);
- connected_free(ifc);
+ connected_free(&ifc);
} else
last = node;
}
@@ -713,7 +759,7 @@ static void if_delete_connected(struct interface *ifp)
last = node;
else {
listnode_delete(ifp->connected, ifc);
- connected_free(ifc);
+ connected_free(&ifc);
}
} else {
last = node;
@@ -783,7 +829,7 @@ void if_delete_update(struct interface *ifp)
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug("interface %s is being deleted from the system",
ifp->name);
- if_delete(ifp);
+ if_delete(&ifp);
}
}
@@ -925,6 +971,47 @@ static void if_down_del_nbr_connected(struct interface *ifp)
}
}
+void if_nhg_dependents_add(struct interface *ifp, struct nhg_hash_entry *nhe)
+{
+ if (ifp->info) {
+ struct zebra_if *zif = (struct zebra_if *)ifp->info;
+
+ nhg_connected_tree_add_nhe(&zif->nhg_dependents, nhe);
+ }
+}
+
+void if_nhg_dependents_del(struct interface *ifp, struct nhg_hash_entry *nhe)
+{
+ if (ifp->info) {
+ struct zebra_if *zif = (struct zebra_if *)ifp->info;
+
+ nhg_connected_tree_del_nhe(&zif->nhg_dependents, nhe);
+ }
+}
+
+unsigned int if_nhg_dependents_count(const struct interface *ifp)
+{
+ if (ifp->info) {
+ struct zebra_if *zif = (struct zebra_if *)ifp->info;
+
+ return nhg_connected_tree_count(&zif->nhg_dependents);
+ }
+
+ return 0;
+}
+
+
+bool if_nhg_dependents_is_empty(const struct interface *ifp)
+{
+ if (ifp->info) {
+ struct zebra_if *zif = (struct zebra_if *)ifp->info;
+
+ return nhg_connected_tree_is_empty(&zif->nhg_dependents);
+ }
+
+ return false;
+}
+
/* Interface is up. */
void if_up(struct interface *ifp)
{
@@ -988,6 +1075,8 @@ void if_down(struct interface *ifp)
zif->down_count++;
quagga_timestamp(2, zif->down_last, sizeof(zif->down_last));
+ if_down_nhg_dependents(ifp);
+
/* Handle interface down for specific types for EVPN. Non-VxLAN
* interfaces
* are checked to see if (remote) neighbor entries need to be purged
@@ -2789,7 +2878,7 @@ static int ip_address_uninstall(struct vty *vty, struct interface *ifp,
if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED)
|| !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) {
listnode_delete(ifp->connected, ifc);
- connected_free(ifc);
+ connected_free(&ifc);
return CMD_WARNING_CONFIG_FAILED;
}
@@ -3014,7 +3103,7 @@ static int ipv6_address_uninstall(struct vty *vty, struct interface *ifp,
if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED)
|| !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) {
listnode_delete(ifp->connected, ifc);
- connected_free(ifc);
+ connected_free(&ifc);
return CMD_WARNING_CONFIG_FAILED;
}
diff --git a/zebra/interface.h b/zebra/interface.h
index e134b9b423..78ccbae623 100644
--- a/zebra/interface.h
+++ b/zebra/interface.h
@@ -27,6 +27,7 @@
#include "hook.h"
#include "zebra/zebra_l2.h"
+#include "zebra/zebra_nhg_private.h"
#ifdef __cplusplus
extern "C" {
@@ -277,6 +278,15 @@ struct zebra_if {
/* Installed addresses chains tree. */
struct route_table *ipv4_subnets;
+ /* Nexthops pointing to this interface */
+ /**
+ * Any nexthop that we get should have an
+ * interface. When an interface goes down,
+ * we will use this list to update the nexthops
+ * pointing to it with that info.
+ */
+ struct nhg_connected_tree_head nhg_dependents;
+
/* Information about up/down changes */
unsigned int up_count;
char up_last[QUAGGA_TIMESTAMP_LEN];
@@ -424,6 +434,14 @@ extern void zebra_if_update_link(struct interface *ifp, ifindex_t link_ifindex,
extern void zebra_if_update_all_links(void);
extern void zebra_if_set_protodown(struct interface *ifp, bool down);
+/* Nexthop group connected functions */
+extern void if_nhg_dependents_add(struct interface *ifp,
+ struct nhg_hash_entry *nhe);
+extern void if_nhg_dependents_del(struct interface *ifp,
+ struct nhg_hash_entry *nhe);
+extern unsigned int if_nhg_dependents_count(const struct interface *ifp);
+extern bool if_nhg_dependents_is_empty(const struct interface *ifp);
+
extern void vrf_add_update(struct vrf *vrfp);
#ifdef HAVE_PROC_NET_DEV
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c
index f52b4746ae..23f1a3bf86 100644
--- a/zebra/kernel_netlink.c
+++ b/zebra/kernel_netlink.c
@@ -99,6 +99,9 @@ static const struct message nlmsg_str[] = {{RTM_NEWROUTE, "RTM_NEWROUTE"},
{RTM_NEWRULE, "RTM_NEWRULE"},
{RTM_DELRULE, "RTM_DELRULE"},
{RTM_GETRULE, "RTM_GETRULE"},
+ {RTM_NEWNEXTHOP, "RTM_NEWNEXTHOP"},
+ {RTM_DELNEXTHOP, "RTM_DELNEXTHOP"},
+ {RTM_GETNEXTHOP, "RTM_GETNEXTHOP"},
{0}};
static const struct message rtproto_str[] = {
@@ -291,6 +294,10 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id,
return netlink_rule_change(h, ns_id, startup);
case RTM_DELRULE:
return netlink_rule_change(h, ns_id, startup);
+ case RTM_NEWNEXTHOP:
+ return netlink_nexthop_change(h, ns_id, startup);
+ case RTM_DELNEXTHOP:
+ return netlink_nexthop_change(h, ns_id, startup);
default:
/*
* If we have received this message then
@@ -884,15 +891,20 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int),
msg_type,
err->msg.nlmsg_seq,
err->msg.nlmsg_pid);
- } else
- flog_err(
- EC_ZEBRA_UNEXPECTED_MESSAGE,
- "%s error: %s, type=%s(%u), seq=%u, pid=%u",
- nl->name,
- safe_strerror(-errnum),
- nl_msg_type_to_str(msg_type),
- msg_type, err->msg.nlmsg_seq,
- err->msg.nlmsg_pid);
+ } else {
+ if ((msg_type != RTM_GETNEXTHOP)
+ || !startup)
+ flog_err(
+ EC_ZEBRA_UNEXPECTED_MESSAGE,
+ "%s error: %s, type=%s(%u), seq=%u, pid=%u",
+ nl->name,
+ safe_strerror(-errnum),
+ nl_msg_type_to_str(
+ msg_type),
+ msg_type,
+ err->msg.nlmsg_seq,
+ err->msg.nlmsg_pid);
+ }
return -1;
}
@@ -1096,7 +1108,8 @@ void kernel_init(struct zebra_ns *zns)
RTMGRP_IPV4_MROUTE |
RTMGRP_NEIGH |
(1 << (RTNLGRP_IPV4_RULE - 1)) |
- (1 << (RTNLGRP_IPV6_RULE - 1));
+ (1 << (RTNLGRP_IPV6_RULE - 1)) |
+ (1 << (RTNLGRP_NEXTHOP - 1));
snprintf(zns->netlink.name, sizeof(zns->netlink.name),
"netlink-listen (NS %u)", zns->ns_id);
diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c
index f5aca2341d..c2812aa47b 100644
--- a/zebra/kernel_socket.c
+++ b/zebra/kernel_socket.c
@@ -1139,16 +1139,17 @@ void rtm_read(struct rt_msghdr *rtm)
*/
if (rtm->rtm_type == RTM_CHANGE)
rib_delete(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL,
- 0, zebra_flags, &p, NULL, NULL, RT_TABLE_MAIN,
- 0, 0, true);
+ 0, zebra_flags, &p, NULL, NULL, 0, RT_TABLE_MAIN, 0,
+ 0, true);
if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD
|| rtm->rtm_type == RTM_CHANGE)
rib_add(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0,
- zebra_flags, &p, NULL, &nh, RT_TABLE_MAIN, 0, 0, 0, 0);
+ zebra_flags, &p, NULL, &nh, 0, RT_TABLE_MAIN,
+ 0, 0, 0, 0);
else
rib_delete(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL,
- 0, zebra_flags, &p, NULL, &nh, RT_TABLE_MAIN,
- 0, 0, true);
+ 0, zebra_flags, &p, NULL, &nh, 0, RT_TABLE_MAIN, 0,
+ 0, true);
}
/* Interface function for the kernel routing table updates. Support
diff --git a/zebra/main.c b/zebra/main.c
index f0225ac5e6..334354eaae 100644
--- a/zebra/main.c
+++ b/zebra/main.c
@@ -165,6 +165,7 @@ static void sigint(void)
}
if (zrouter.lsp_process_q)
work_queue_free_and_null(&zrouter.lsp_process_q);
+
vrf_terminate();
ns_walk_func(zebra_ns_early_shutdown);
diff --git a/zebra/redistribute.c b/zebra/redistribute.c
index 0dc9de0c59..4e0163f8ac 100644
--- a/zebra/redistribute.c
+++ b/zebra/redistribute.c
@@ -643,7 +643,7 @@ int zebra_add_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn,
afi = family2afi(rn->p.family);
if (rmap_name)
ret = zebra_import_table_route_map_check(
- afi, re->type, re->instance, &rn->p, re->ng.nexthop,
+ afi, re->type, re->instance, &rn->p, re->ng->nexthop,
zvrf->vrf->vrf_id, re->tag, rmap_name);
if (ret != RMAP_PERMITMATCH) {
@@ -676,10 +676,10 @@ int zebra_add_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn,
newre->metric = re->metric;
newre->mtu = re->mtu;
newre->table = zvrf->table_id;
- newre->nexthop_num = 0;
newre->uptime = monotime(NULL);
newre->instance = re->table;
- route_entry_copy_nexthops(newre, re->ng.nexthop);
+ newre->ng = nexthop_group_new();
+ route_entry_copy_nexthops(newre, re->ng->nexthop);
rib_add_multipath(afi, SAFI_UNICAST, &p, NULL, newre);
@@ -696,7 +696,7 @@ int zebra_del_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn,
prefix_copy(&p, &rn->p);
rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_TABLE,
- re->table, re->flags, &p, NULL, re->ng.nexthop,
+ re->table, re->flags, &p, NULL, re->ng->nexthop, re->nhe_id,
zvrf->table_id, re->metric, re->distance, false);
return 0;
diff --git a/zebra/rib.h b/zebra/rib.h
index ee1df89c0e..35aa011c0d 100644
--- a/zebra/rib.h
+++ b/zebra/rib.h
@@ -88,11 +88,14 @@ struct route_entry {
struct re_list_item next;
/* Nexthop structure (from RIB) */
- struct nexthop_group ng;
+ struct nexthop_group *ng;
/* Nexthop group from FIB (optional) */
struct nexthop_group fib_ng;
+ /* Nexthop group hash entry ID */
+ uint32_t nhe_id;
+
/* Tag */
route_tag_t tag;
@@ -135,10 +138,6 @@ struct route_entry {
/* Route has Failed installation into the Data Plane in some manner */
#define ROUTE_ENTRY_FAILED 0x20
- /* Nexthop information. */
- uint8_t nexthop_num;
- uint8_t nexthop_active_num;
-
/* Sequence value incremented for each dataplane operation */
uint32_t dplane_sequence;
@@ -154,13 +153,14 @@ struct route_entry {
#define RIB_KERNEL_ROUTE(R) RKERNEL_ROUTE((R)->type)
/* meta-queue structure:
- * sub-queue 0: connected, kernel
- * sub-queue 1: static
- * sub-queue 2: RIP, RIPng, OSPF, OSPF6, IS-IS, EIGRP, NHRP
- * sub-queue 3: iBGP, eBGP
- * sub-queue 4: any other origin (if any)
+ * sub-queue 0: nexthop group objects
+ * sub-queue 1: connected, kernel
+ * sub-queue 2: static
+ * sub-queue 3: RIP, RIPng, OSPF, OSPF6, IS-IS, EIGRP, NHRP
+ * sub-queue 4: iBGP, eBGP
+ * sub-queue 5: any other origin (if any)
*/
-#define MQ_SIZE 5
+#define MQ_SIZE 6
struct meta_queue {
struct list *subq[MQ_SIZE];
uint32_t size; /* sum of lengths of all subqueues */
@@ -210,7 +210,7 @@ DECLARE_LIST(re_list, struct route_entry, next);
#define RIB_ROUTE_QUEUED(x) (1 << (x))
// If MQ_SIZE is modified this value needs to be updated.
-#define RIB_ROUTE_ANY_QUEUED 0x1F
+#define RIB_ROUTE_ANY_QUEUED 0x3F
/*
* The maximum qindex that can be used.
@@ -364,8 +364,8 @@ extern void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re);
extern int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
unsigned short instance, int flags, struct prefix *p,
struct prefix_ipv6 *src_p, const struct nexthop *nh,
- uint32_t table_id, uint32_t metric, uint32_t mtu,
- uint8_t distance, route_tag_t tag);
+ uint32_t nhe_id, uint32_t table_id, uint32_t metric,
+ uint32_t mtu, uint8_t distance, route_tag_t tag);
extern int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
struct prefix_ipv6 *src_p, struct route_entry *re);
@@ -373,8 +373,8 @@ extern int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
extern void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
unsigned short instance, int flags, struct prefix *p,
struct prefix_ipv6 *src_p, const struct nexthop *nh,
- uint32_t table_id, uint32_t metric, uint8_t distance,
- bool fromkernel);
+ uint32_t nhe_id, uint32_t table_id, uint32_t metric,
+ uint8_t distance, bool fromkernel);
extern struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id,
union g_addr *addr,
@@ -398,7 +398,13 @@ extern unsigned long rib_score_proto(uint8_t proto, unsigned short instance);
extern unsigned long rib_score_proto_table(uint8_t proto,
unsigned short instance,
struct route_table *table);
-extern void rib_queue_add(struct route_node *rn);
+
+extern int rib_queue_add(struct route_node *rn);
+
+struct nhg_ctx; /* Forward declaration */
+
+extern int rib_queue_nhg_add(struct nhg_ctx *ctx);
+
extern void meta_queue_free(struct meta_queue *mq);
extern int zebra_rib_labeled_unicast(struct route_entry *re);
extern struct route_table *rib_table_ipv6;
@@ -527,7 +533,7 @@ static inline struct nexthop_group *rib_active_nhg(struct route_entry *re)
if (re->fib_ng.nexthop)
return &(re->fib_ng);
else
- return &(re->ng);
+ return re->ng;
}
extern void zebra_vty_init(void);
diff --git a/zebra/rt.h b/zebra/rt.h
index f311a6b9d3..4b9a3f83fe 100644
--- a/zebra/rt.h
+++ b/zebra/rt.h
@@ -40,13 +40,17 @@ extern "C" {
#define RSYSTEM_ROUTE(type) \
((RKERNEL_ROUTE(type)) || (type) == ZEBRA_ROUTE_CONNECT)
+
/*
- * Update or delete a route, LSP, pseudowire, or vxlan MAC from the kernel,
- * using info from a dataplane context.
+ * Update or delete a route, nexthop, LSP, pseudowire, or vxlan MAC from the
+ * kernel, using info from a dataplane context.
*/
extern enum zebra_dplane_result kernel_route_update(
struct zebra_dplane_ctx *ctx);
+extern enum zebra_dplane_result
+kernel_nexthop_update(struct zebra_dplane_ctx *ctx);
+
extern enum zebra_dplane_result kernel_lsp_update(
struct zebra_dplane_ctx *ctx);
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index 43e44cad16..915ad1a10c 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -27,6 +27,7 @@
#include <linux/mpls_iptunnel.h>
#include <linux/neighbour.h>
#include <linux/rtnetlink.h>
+#include <linux/nexthop.h>
/* Hack for GNU libc version 2. */
#ifndef MSG_TRUNC
@@ -49,6 +50,7 @@
#include "vty.h"
#include "mpls.h"
#include "vxlan.h"
+#include "printfrr.h"
#include "zebra/zapi_msg.h"
#include "zebra/zebra_ns.h"
@@ -62,6 +64,7 @@
#include "zebra/zebra_mpls.h"
#include "zebra/kernel_netlink.h"
#include "zebra/rt_netlink.h"
+#include "zebra/zebra_nhg.h"
#include "zebra/zebra_mroute.h"
#include "zebra/zebra_vxlan.h"
#include "zebra/zebra_errors.h"
@@ -72,6 +75,8 @@
static vlanid_t filter_vlan = 0;
+static bool supports_nh;
+
struct gw_family_t {
uint16_t filler;
uint16_t family;
@@ -186,6 +191,7 @@ static inline int zebra2proto(int proto)
proto = RTPROT_OPENFABRIC;
break;
case ZEBRA_ROUTE_TABLE:
+ case ZEBRA_ROUTE_NHG:
proto = RTPROT_ZEBRA;
break;
default:
@@ -205,7 +211,7 @@ static inline int zebra2proto(int proto)
return proto;
}
-static inline int proto2zebra(int proto, int family)
+static inline int proto2zebra(int proto, int family, bool is_nexthop)
{
switch (proto) {
case RTPROT_BABEL:
@@ -249,6 +255,12 @@ static inline int proto2zebra(int proto, int family)
case RTPROT_OPENFABRIC:
proto = ZEBRA_ROUTE_OPENFABRIC;
break;
+ case RTPROT_ZEBRA:
+ if (is_nexthop) {
+ proto = ZEBRA_ROUTE_NHG;
+ break;
+ }
+ /* Intentional fall thru */
default:
/*
* When a user adds a new protocol this will show up
@@ -319,6 +331,169 @@ static int parse_encap_mpls(struct rtattr *tb, mpls_label_t *labels)
return num_labels;
}
+static struct nexthop
+parse_nexthop_unicast(ns_id_t ns_id, struct rtmsg *rtm, struct rtattr **tb,
+ enum blackhole_type bh_type, int index, void *prefsrc,
+ void *gate, afi_t afi, vrf_id_t vrf_id)
+{
+ struct interface *ifp = NULL;
+ struct nexthop nh = {0};
+ mpls_label_t labels[MPLS_MAX_LABELS] = {0};
+ int num_labels = 0;
+
+ vrf_id_t nh_vrf_id = vrf_id;
+ size_t sz = (afi == AFI_IP) ? 4 : 16;
+
+ if (bh_type == BLACKHOLE_UNSPEC) {
+ if (index && !gate)
+ nh.type = NEXTHOP_TYPE_IFINDEX;
+ else if (index && gate)
+ nh.type = (afi == AFI_IP) ? NEXTHOP_TYPE_IPV4_IFINDEX
+ : NEXTHOP_TYPE_IPV6_IFINDEX;
+ else if (!index && gate)
+ nh.type = (afi == AFI_IP) ? NEXTHOP_TYPE_IPV4
+ : NEXTHOP_TYPE_IPV6;
+ else {
+ nh.type = NEXTHOP_TYPE_BLACKHOLE;
+ nh.bh_type = bh_type;
+ }
+ } else {
+ nh.type = NEXTHOP_TYPE_BLACKHOLE;
+ nh.bh_type = bh_type;
+ }
+ nh.ifindex = index;
+ if (prefsrc)
+ memcpy(&nh.src, prefsrc, sz);
+ if (gate)
+ memcpy(&nh.gate, gate, sz);
+
+ if (index) {
+ ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), index);
+ if (ifp)
+ nh_vrf_id = ifp->vrf_id;
+ }
+ nh.vrf_id = nh_vrf_id;
+
+ if (tb[RTA_ENCAP] && tb[RTA_ENCAP_TYPE]
+ && *(uint16_t *)RTA_DATA(tb[RTA_ENCAP_TYPE])
+ == LWTUNNEL_ENCAP_MPLS) {
+ num_labels = parse_encap_mpls(tb[RTA_ENCAP], labels);
+ }
+
+ if (rtm->rtm_flags & RTNH_F_ONLINK)
+ SET_FLAG(nh.flags, NEXTHOP_FLAG_ONLINK);
+
+ if (num_labels)
+ nexthop_add_labels(&nh, ZEBRA_LSP_STATIC, num_labels, labels);
+
+ return nh;
+}
+
+static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id,
+ struct route_entry *re,
+ struct rtmsg *rtm,
+ struct rtnexthop *rtnh,
+ struct rtattr **tb,
+ void *prefsrc, vrf_id_t vrf_id)
+{
+ void *gate = NULL;
+ struct interface *ifp = NULL;
+ int index = 0;
+ /* MPLS labels */
+ mpls_label_t labels[MPLS_MAX_LABELS] = {0};
+ int num_labels = 0;
+ struct rtattr *rtnh_tb[RTA_MAX + 1] = {};
+
+ int len = RTA_PAYLOAD(tb[RTA_MULTIPATH]);
+ vrf_id_t nh_vrf_id = vrf_id;
+
+ re->ng = nexthop_group_new();
+
+ for (;;) {
+ struct nexthop *nh = NULL;
+
+ if (len < (int)sizeof(*rtnh) || rtnh->rtnh_len > len)
+ break;
+
+ index = rtnh->rtnh_ifindex;
+ if (index) {
+ /*
+ * Yes we are looking this up
+ * for every nexthop and just
+ * using the last one looked
+ * up right now
+ */
+ ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id),
+ index);
+ if (ifp)
+ nh_vrf_id = ifp->vrf_id;
+ else {
+ flog_warn(
+ EC_ZEBRA_UNKNOWN_INTERFACE,
+ "%s: Unknown interface %u specified, defaulting to VRF_DEFAULT",
+ __PRETTY_FUNCTION__, index);
+ nh_vrf_id = VRF_DEFAULT;
+ }
+ } else
+ nh_vrf_id = vrf_id;
+
+ if (rtnh->rtnh_len > sizeof(*rtnh)) {
+ memset(rtnh_tb, 0, sizeof(rtnh_tb));
+
+ netlink_parse_rtattr(rtnh_tb, RTA_MAX, RTNH_DATA(rtnh),
+ rtnh->rtnh_len - sizeof(*rtnh));
+ if (rtnh_tb[RTA_GATEWAY])
+ gate = RTA_DATA(rtnh_tb[RTA_GATEWAY]);
+ if (rtnh_tb[RTA_ENCAP] && rtnh_tb[RTA_ENCAP_TYPE]
+ && *(uint16_t *)RTA_DATA(rtnh_tb[RTA_ENCAP_TYPE])
+ == LWTUNNEL_ENCAP_MPLS) {
+ num_labels = parse_encap_mpls(
+ rtnh_tb[RTA_ENCAP], labels);
+ }
+ }
+
+ if (gate && rtm->rtm_family == AF_INET) {
+ if (index)
+ nh = route_entry_nexthop_ipv4_ifindex_add(
+ re, gate, prefsrc, index, nh_vrf_id);
+ else
+ nh = route_entry_nexthop_ipv4_add(
+ re, gate, prefsrc, nh_vrf_id);
+ } else if (gate && rtm->rtm_family == AF_INET6) {
+ if (index)
+ nh = route_entry_nexthop_ipv6_ifindex_add(
+ re, gate, index, nh_vrf_id);
+ else
+ nh = route_entry_nexthop_ipv6_add(re, gate,
+ nh_vrf_id);
+ } else
+ nh = route_entry_nexthop_ifindex_add(re, index,
+ nh_vrf_id);
+
+ if (nh) {
+ if (num_labels)
+ nexthop_add_labels(nh, ZEBRA_LSP_STATIC,
+ num_labels, labels);
+
+ if (rtnh->rtnh_flags & RTNH_F_ONLINK)
+ SET_FLAG(nh->flags, NEXTHOP_FLAG_ONLINK);
+ }
+
+ if (rtnh->rtnh_len == 0)
+ break;
+
+ len -= NLMSG_ALIGN(rtnh->rtnh_len);
+ rtnh = RTNH_NEXT(rtnh);
+ }
+
+ uint8_t nhop_num = nexthop_group_nexthop_num(re->ng);
+
+ if (!nhop_num)
+ nexthop_group_delete(&re->ng);
+
+ return nhop_num;
+}
+
/* Looking up routing table by netlink interface. */
static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
int startup)
@@ -340,6 +515,7 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
uint32_t mtu = 0;
uint8_t distance = 0;
route_tag_t tag = 0;
+ uint32_t nhe_id = 0;
void *dest = NULL;
void *gate = NULL;
@@ -347,10 +523,6 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
void *src = NULL; /* IPv6 srcdest source prefix */
enum blackhole_type bh_type = BLACKHOLE_UNSPEC;
- /* MPLS labels */
- mpls_label_t labels[MPLS_MAX_LABELS] = {0};
- int num_labels = 0;
-
rtm = NLMSG_DATA(h);
if (startup && h->nlmsg_type != RTM_NEWROUTE)
@@ -423,7 +595,7 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
/* Route which inserted by Zebra. */
if (is_selfroute(rtm->rtm_protocol)) {
flags |= ZEBRA_FLAG_SELFROUTE;
- proto = proto2zebra(rtm->rtm_protocol, rtm->rtm_family);
+ proto = proto2zebra(rtm->rtm_protocol, rtm->rtm_family, false);
}
if (tb[RTA_OIF])
index = *(int *)RTA_DATA(tb[RTA_OIF]);
@@ -444,6 +616,9 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
if (tb[RTA_GATEWAY])
gate = RTA_DATA(tb[RTA_GATEWAY]);
+ if (tb[RTA_NH_ID])
+ nhe_id = *(uint32_t *)RTA_DATA(tb[RTA_NH_ID]);
+
if (tb[RTA_PRIORITY])
metric = *(int *)RTA_DATA(tb[RTA_PRIORITY]);
@@ -547,75 +722,24 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
afi = AFI_IP6;
if (h->nlmsg_type == RTM_NEWROUTE) {
- struct interface *ifp;
- vrf_id_t nh_vrf_id = vrf_id;
if (!tb[RTA_MULTIPATH]) {
- struct nexthop nh;
- size_t sz = (afi == AFI_IP) ? 4 : 16;
-
- memset(&nh, 0, sizeof(nh));
-
- if (bh_type == BLACKHOLE_UNSPEC) {
- if (index && !gate)
- nh.type = NEXTHOP_TYPE_IFINDEX;
- else if (index && gate)
- nh.type =
- (afi == AFI_IP)
- ? NEXTHOP_TYPE_IPV4_IFINDEX
- : NEXTHOP_TYPE_IPV6_IFINDEX;
- else if (!index && gate)
- nh.type = (afi == AFI_IP)
- ? NEXTHOP_TYPE_IPV4
- : NEXTHOP_TYPE_IPV6;
- else {
- nh.type = NEXTHOP_TYPE_BLACKHOLE;
- nh.bh_type = bh_type;
- }
- } else {
- nh.type = NEXTHOP_TYPE_BLACKHOLE;
- nh.bh_type = bh_type;
- }
- nh.ifindex = index;
- if (prefsrc)
- memcpy(&nh.src, prefsrc, sz);
- if (gate)
- memcpy(&nh.gate, gate, sz);
-
- if (index) {
- ifp = if_lookup_by_index_per_ns(
- zebra_ns_lookup(ns_id),
- index);
- if (ifp)
- nh_vrf_id = ifp->vrf_id;
- }
- nh.vrf_id = nh_vrf_id;
+ struct nexthop nh = {0};
- if (tb[RTA_ENCAP] && tb[RTA_ENCAP_TYPE]
- && *(uint16_t *)RTA_DATA(tb[RTA_ENCAP_TYPE])
- == LWTUNNEL_ENCAP_MPLS) {
- num_labels =
- parse_encap_mpls(tb[RTA_ENCAP], labels);
+ if (!nhe_id) {
+ nh = parse_nexthop_unicast(
+ ns_id, rtm, tb, bh_type, index, prefsrc,
+ gate, afi, vrf_id);
}
-
- if (rtm->rtm_flags & RTNH_F_ONLINK)
- SET_FLAG(nh.flags, NEXTHOP_FLAG_ONLINK);
-
- if (num_labels)
- nexthop_add_labels(&nh, ZEBRA_LSP_STATIC,
- num_labels, labels);
-
rib_add(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, &p,
- &src_p, &nh, table, metric, mtu, distance, tag);
+ &src_p, &nh, nhe_id, table, metric, mtu,
+ distance, tag);
} else {
/* This is a multipath route */
-
struct route_entry *re;
struct rtnexthop *rtnh =
(struct rtnexthop *)RTA_DATA(tb[RTA_MULTIPATH]);
- len = RTA_PAYLOAD(tb[RTA_MULTIPATH]);
-
re = XCALLOC(MTYPE_RE, sizeof(struct route_entry));
re->type = proto;
re->distance = distance;
@@ -624,148 +748,73 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
re->mtu = mtu;
re->vrf_id = vrf_id;
re->table = table;
- re->nexthop_num = 0;
re->uptime = monotime(NULL);
re->tag = tag;
+ re->nhe_id = nhe_id;
- for (;;) {
- struct nexthop *nh = NULL;
-
- if (len < (int)sizeof(*rtnh)
- || rtnh->rtnh_len > len)
- break;
-
- index = rtnh->rtnh_ifindex;
- if (index) {
- /*
- * Yes we are looking this up
- * for every nexthop and just
- * using the last one looked
- * up right now
- */
- ifp = if_lookup_by_index_per_ns(
- zebra_ns_lookup(ns_id),
- index);
- if (ifp)
- nh_vrf_id = ifp->vrf_id;
- else {
- flog_warn(
- EC_ZEBRA_UNKNOWN_INTERFACE,
- "%s: Unknown interface %u specified, defaulting to VRF_DEFAULT",
- __PRETTY_FUNCTION__,
- index);
- nh_vrf_id = VRF_DEFAULT;
- }
- } else
- nh_vrf_id = vrf_id;
-
- gate = 0;
- if (rtnh->rtnh_len > sizeof(*rtnh)) {
- memset(tb, 0, sizeof(tb));
- netlink_parse_rtattr(
- tb, RTA_MAX, RTNH_DATA(rtnh),
- rtnh->rtnh_len - sizeof(*rtnh));
- if (tb[RTA_GATEWAY])
- gate = RTA_DATA(
- tb[RTA_GATEWAY]);
- if (tb[RTA_ENCAP] && tb[RTA_ENCAP_TYPE]
- && *(uint16_t *)RTA_DATA(
- tb[RTA_ENCAP_TYPE])
- == LWTUNNEL_ENCAP_MPLS) {
- num_labels = parse_encap_mpls(
- tb[RTA_ENCAP], labels);
- }
- }
-
- if (gate) {
- if (rtm->rtm_family == AF_INET) {
- if (index)
- nh = route_entry_nexthop_ipv4_ifindex_add(
- re, gate,
- prefsrc, index,
- nh_vrf_id);
- else
- nh = route_entry_nexthop_ipv4_add(
- re, gate,
- prefsrc,
- nh_vrf_id);
- } else if (rtm->rtm_family
- == AF_INET6) {
- if (index)
- nh = route_entry_nexthop_ipv6_ifindex_add(
- re, gate, index,
- nh_vrf_id);
- else
- nh = route_entry_nexthop_ipv6_add(
- re, gate,
- nh_vrf_id);
- }
- } else
- nh = route_entry_nexthop_ifindex_add(
- re, index, nh_vrf_id);
-
- if (nh && num_labels)
- nexthop_add_labels(nh, ZEBRA_LSP_STATIC,
- num_labels, labels);
+ if (!nhe_id) {
+ uint8_t nhop_num =
+ parse_multipath_nexthops_unicast(
+ ns_id, re, rtm, rtnh, tb,
+ prefsrc, vrf_id);
- if (nh && (rtnh->rtnh_flags & RTNH_F_ONLINK))
- SET_FLAG(nh->flags,
- NEXTHOP_FLAG_ONLINK);
-
- if (rtnh->rtnh_len == 0)
- break;
-
- len -= NLMSG_ALIGN(rtnh->rtnh_len);
- rtnh = RTNH_NEXT(rtnh);
+ zserv_nexthop_num_warn(
+ __func__, (const struct prefix *)&p,
+ nhop_num);
}
- zserv_nexthop_num_warn(__func__,
- (const struct prefix *)&p,
- re->nexthop_num);
- if (re->nexthop_num == 0)
- XFREE(MTYPE_RE, re);
- else
+ if (nhe_id || re->ng)
rib_add_multipath(afi, SAFI_UNICAST, &p,
&src_p, re);
+ else
+ XFREE(MTYPE_RE, re);
}
} else {
- if (!tb[RTA_MULTIPATH]) {
- struct nexthop nh;
- size_t sz = (afi == AFI_IP) ? 4 : 16;
-
- memset(&nh, 0, sizeof(nh));
- if (bh_type == BLACKHOLE_UNSPEC) {
- if (index && !gate)
- nh.type = NEXTHOP_TYPE_IFINDEX;
- else if (index && gate)
- nh.type =
- (afi == AFI_IP)
- ? NEXTHOP_TYPE_IPV4_IFINDEX
- : NEXTHOP_TYPE_IPV6_IFINDEX;
- else if (!index && gate)
- nh.type = (afi == AFI_IP)
- ? NEXTHOP_TYPE_IPV4
- : NEXTHOP_TYPE_IPV6;
- else {
+ if (nhe_id) {
+ rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags,
+ &p, &src_p, NULL, nhe_id, table, metric,
+ distance, true);
+ } else {
+ if (!tb[RTA_MULTIPATH]) {
+ struct nexthop nh;
+ size_t sz = (afi == AFI_IP) ? 4 : 16;
+
+ memset(&nh, 0, sizeof(nh));
+ if (bh_type == BLACKHOLE_UNSPEC) {
+ if (index && !gate)
+ nh.type = NEXTHOP_TYPE_IFINDEX;
+ else if (index && gate)
+ nh.type =
+ (afi == AFI_IP)
+ ? NEXTHOP_TYPE_IPV4_IFINDEX
+ : NEXTHOP_TYPE_IPV6_IFINDEX;
+ else if (!index && gate)
+ nh.type =
+ (afi == AFI_IP)
+ ? NEXTHOP_TYPE_IPV4
+ : NEXTHOP_TYPE_IPV6;
+ else {
+ nh.type =
+ NEXTHOP_TYPE_BLACKHOLE;
+ nh.bh_type = BLACKHOLE_UNSPEC;
+ }
+ } else {
nh.type = NEXTHOP_TYPE_BLACKHOLE;
- nh.bh_type = BLACKHOLE_UNSPEC;
+ nh.bh_type = bh_type;
}
+ nh.ifindex = index;
+ if (gate)
+ memcpy(&nh.gate, gate, sz);
+ rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0,
+ flags, &p, &src_p, &nh, 0, table,
+ metric, distance, true);
} else {
- nh.type = NEXTHOP_TYPE_BLACKHOLE;
- nh.bh_type = bh_type;
+ /* XXX: need to compare the entire list of
+ * nexthops here for NLM_F_APPEND stupidity */
+ rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0,
+ flags, &p, &src_p, NULL, 0, table,
+ metric, distance, true);
}
- nh.ifindex = index;
- if (gate)
- memcpy(&nh.gate, gate, sz);
- rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags,
- &p, &src_p, &nh, table, metric, distance,
- true);
- } else {
- /* XXX: need to compare the entire list of nexthops
- * here for NLM_F_APPEND stupidity */
- rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags,
- &p, &src_p, NULL, table, metric, distance,
- true);
}
}
@@ -1023,6 +1072,35 @@ static void _netlink_route_rta_add_gateway_info(uint8_t route_family,
}
}
+static int build_label_stack(struct mpls_label_stack *nh_label,
+ mpls_lse_t *out_lse, char *label_buf,
+ size_t label_buf_size)
+{
+ char label_buf1[20];
+ int num_labels = 0;
+
+ for (int i = 0; nh_label && i < nh_label->num_labels; i++) {
+ if (nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL)
+ continue;
+
+ if (IS_ZEBRA_DEBUG_KERNEL) {
+ if (!num_labels)
+ sprintf(label_buf, "label %u",
+ nh_label->label[i]);
+ else {
+ sprintf(label_buf1, "/%u", nh_label->label[i]);
+ strlcat(label_buf, label_buf1, label_buf_size);
+ }
+ }
+
+ out_lse[num_labels] =
+ mpls_lse_encode(nh_label->label[i], 0, 0, 0);
+ num_labels++;
+ }
+
+ return num_labels;
+}
+
/* This function takes a nexthop as argument and adds
* the appropriate netlink attributes to an existing
* netlink message.
@@ -1040,10 +1118,12 @@ static void _netlink_route_build_singlepath(const char *routedesc, int bytelen,
struct rtmsg *rtmsg,
size_t req_size, int cmd)
{
- struct mpls_label_stack *nh_label;
+
mpls_lse_t out_lse[MPLS_MAX_LABELS];
- int num_labels = 0;
char label_buf[256];
+ int num_labels = 0;
+
+ assert(nexthop);
/*
* label_buf is *only* currently used within debugging.
@@ -1053,30 +1133,8 @@ static void _netlink_route_build_singlepath(const char *routedesc, int bytelen,
*/
label_buf[0] = '\0';
- assert(nexthop);
- char label_buf1[20];
-
- nh_label = nexthop->nh_label;
-
- for (int i = 0; nh_label && i < nh_label->num_labels; i++) {
- if (nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL)
- continue;
-
- if (IS_ZEBRA_DEBUG_KERNEL) {
- if (!num_labels)
- sprintf(label_buf, "label %u",
- nh_label->label[i]);
- else {
- sprintf(label_buf1, "/%u", nh_label->label[i]);
- strlcat(label_buf, label_buf1,
- sizeof(label_buf));
- }
- }
-
- out_lse[num_labels] =
- mpls_lse_encode(nh_label->label[i], 0, 0, 0);
- num_labels++;
- }
+ num_labels = build_label_stack(nexthop->nh_label, out_lse, label_buf,
+ sizeof(label_buf));
if (num_labels) {
/* Set the BoS bit */
@@ -1221,16 +1279,17 @@ static void _netlink_route_build_multipath(const char *routedesc, int bytelen,
struct rtmsg *rtmsg,
const union g_addr **src)
{
- struct mpls_label_stack *nh_label;
mpls_lse_t out_lse[MPLS_MAX_LABELS];
- int num_labels = 0;
char label_buf[256];
+ int num_labels = 0;
rtnh->rtnh_len = sizeof(*rtnh);
rtnh->rtnh_flags = 0;
rtnh->rtnh_hops = 0;
rta->rta_len += rtnh->rtnh_len;
+ assert(nexthop);
+
/*
* label_buf is *only* currently used within debugging.
* As such when we assign it we are guarding it inside
@@ -1239,30 +1298,8 @@ static void _netlink_route_build_multipath(const char *routedesc, int bytelen,
*/
label_buf[0] = '\0';
- assert(nexthop);
- char label_buf1[20];
-
- nh_label = nexthop->nh_label;
-
- for (int i = 0; nh_label && i < nh_label->num_labels; i++) {
- if (nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL)
- continue;
-
- if (IS_ZEBRA_DEBUG_KERNEL) {
- if (!num_labels)
- sprintf(label_buf, "label %u",
- nh_label->label[i]);
- else {
- sprintf(label_buf1, "/%u", nh_label->label[i]);
- strlcat(label_buf, label_buf1,
- sizeof(label_buf));
- }
- }
-
- out_lse[num_labels] =
- mpls_lse_encode(nh_label->label[i], 0, 0, 0);
- num_labels++;
- }
+ num_labels = build_label_stack(nexthop->nh_label, out_lse, label_buf,
+ sizeof(label_buf));
if (num_labels) {
/* Set the BoS bit */
@@ -1430,6 +1467,13 @@ static void _netlink_route_debug(int cmd, const struct prefix *p,
}
}
+static void _netlink_nexthop_debug(int cmd, uint32_t id)
+{
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("netlink_nexthop(): %s, id=%u",
+ nl_msg_type_to_str(cmd), id);
+}
+
static void _netlink_mpls_debug(int cmd, uint32_t label, const char *routedesc)
{
if (IS_ZEBRA_DEBUG_KERNEL)
@@ -1595,6 +1639,13 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
RTA_PAYLOAD(rta));
}
+ if (supports_nh) {
+ /* Kernel supports nexthop objects */
+ addattr32(&req.n, sizeof(req), RTA_NH_ID,
+ dplane_ctx_get_nhe_id(ctx));
+ goto skip;
+ }
+
/* Count overall nexthops so we can decide whether to use singlepath
* or multipath case.
*/
@@ -1842,6 +1893,262 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in)
return suc;
}
+/* Char length to debug ID with */
+#define ID_LENGTH 10
+
+static void _netlink_nexthop_build_group(struct nlmsghdr *n, size_t req_size,
+ uint32_t id,
+ const struct nh_grp *z_grp,
+ const uint8_t count)
+{
+ struct nexthop_grp grp[count];
+ /* Need space for max group size, "/", and null term */
+ char buf[(MULTIPATH_NUM * (ID_LENGTH + 1)) + 1];
+ char buf1[ID_LENGTH + 2];
+
+ buf[0] = '\0';
+
+ memset(grp, 0, sizeof(grp));
+
+ if (count) {
+ for (int i = 0; i < count; i++) {
+ grp[i].id = z_grp[i].id;
+ grp[i].weight = z_grp[i].weight;
+
+ if (IS_ZEBRA_DEBUG_KERNEL) {
+ if (i == 0)
+ snprintf(buf, sizeof(buf1), "group %u",
+ grp[i].id);
+ else {
+ snprintf(buf1, sizeof(buf1), "/%u",
+ grp[i].id);
+ strlcat(buf, buf1, sizeof(buf));
+ }
+ }
+ }
+ addattr_l(n, req_size, NHA_GROUP, grp, count * sizeof(*grp));
+ }
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: ID (%u): %s", __func__, id, buf);
+}
+
+/**
+ * netlink_nexthop() - Nexthop change via the netlink interface
+ *
+ * @ctx: Dataplane ctx
+ *
+ * Return: Result status
+ */
+static int netlink_nexthop(int cmd, struct zebra_dplane_ctx *ctx)
+{
+ struct {
+ struct nlmsghdr n;
+ struct nhmsg nhm;
+ char buf[NL_PKT_BUF_SIZE];
+ } req;
+
+ mpls_lse_t out_lse[MPLS_MAX_LABELS];
+ char label_buf[256];
+ int num_labels = 0;
+ size_t req_size = sizeof(req);
+
+ /* Nothing to do if the kernel doesn't support nexthop objects */
+ if (!supports_nh)
+ return 0;
+
+ label_buf[0] = '\0';
+
+ memset(&req, 0, req_size);
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg));
+ req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
+
+ if (cmd == RTM_NEWNEXTHOP)
+ req.n.nlmsg_flags |= NLM_F_REPLACE;
+
+ req.n.nlmsg_type = cmd;
+ req.n.nlmsg_pid = dplane_ctx_get_ns(ctx)->nls.snl.nl_pid;
+
+ req.nhm.nh_family = AF_UNSPEC;
+ /* TODO: Scope? */
+
+ uint32_t id = dplane_ctx_get_nhe_id(ctx);
+
+ if (!id) {
+ flog_err(
+ EC_ZEBRA_NHG_FIB_UPDATE,
+ "Failed trying to update a nexthop group in the kernel that does not have an ID");
+ return -1;
+ }
+
+ addattr32(&req.n, req_size, NHA_ID, id);
+
+ if (cmd == RTM_NEWNEXTHOP) {
+ if (dplane_ctx_get_nhe_nh_grp_count(ctx))
+ _netlink_nexthop_build_group(
+ &req.n, req_size, id,
+ dplane_ctx_get_nhe_nh_grp(ctx),
+ dplane_ctx_get_nhe_nh_grp_count(ctx));
+ else {
+ const struct nexthop *nh =
+ dplane_ctx_get_nhe_ng(ctx)->nexthop;
+ afi_t afi = dplane_ctx_get_nhe_afi(ctx);
+
+ if (afi == AFI_IP)
+ req.nhm.nh_family = AF_INET;
+ else if (afi == AFI_IP6)
+ req.nhm.nh_family = AF_INET6;
+
+ switch (nh->type) {
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ addattr_l(&req.n, req_size, NHA_GATEWAY,
+ &nh->gate.ipv4, IPV4_MAX_BYTELEN);
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ addattr_l(&req.n, req_size, NHA_GATEWAY,
+ &nh->gate.ipv6, IPV6_MAX_BYTELEN);
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ addattr_l(&req.n, req_size, NHA_BLACKHOLE, NULL,
+ 0);
+ /* Blackhole shouldn't have anymore attributes
+ */
+ goto nexthop_done;
+ case NEXTHOP_TYPE_IFINDEX:
+ /* Don't need anymore info for this */
+ break;
+ }
+
+ if (!nh->ifindex) {
+ flog_err(
+ EC_ZEBRA_NHG_FIB_UPDATE,
+ "Context received for kernel nexthop update without an interface");
+ return -1;
+ }
+
+ addattr32(&req.n, req_size, NHA_OIF, nh->ifindex);
+
+ num_labels =
+ build_label_stack(nh->nh_label, out_lse,
+ label_buf, sizeof(label_buf));
+
+ if (num_labels) {
+ /* Set the BoS bit */
+ out_lse[num_labels - 1] |=
+ htonl(1 << MPLS_LS_S_SHIFT);
+
+ /*
+ * TODO: MPLS unsupported for now in kernel.
+ */
+ if (req.nhm.nh_family == AF_MPLS)
+ goto nexthop_done;
+#if 0
+ addattr_l(&req.n, req_size, NHA_NEWDST,
+ &out_lse,
+ num_labels
+ * sizeof(mpls_lse_t));
+#endif
+ else {
+ struct rtattr *nest;
+ uint16_t encap = LWTUNNEL_ENCAP_MPLS;
+
+ addattr_l(&req.n, req_size,
+ NHA_ENCAP_TYPE, &encap,
+ sizeof(uint16_t));
+ nest = addattr_nest(&req.n, req_size,
+ NHA_ENCAP);
+ addattr_l(&req.n, req_size,
+ MPLS_IPTUNNEL_DST, &out_lse,
+ num_labels
+ * sizeof(mpls_lse_t));
+ addattr_nest_end(&req.n, nest);
+ }
+ }
+
+ nexthop_done:
+ if (IS_ZEBRA_DEBUG_KERNEL) {
+ char buf[NEXTHOP_STRLEN];
+
+ snprintfrr(buf, sizeof(buf), "%pNHv", nh);
+ zlog_debug("%s: ID (%u): %s (%u) %s ", __func__,
+ id, buf, nh->vrf_id, label_buf);
+ }
+ }
+
+ req.nhm.nh_protocol = zebra2proto(dplane_ctx_get_nhe_type(ctx));
+
+ } else if (cmd != RTM_DELNEXTHOP) {
+ flog_err(
+ EC_ZEBRA_NHG_FIB_UPDATE,
+ "Nexthop group kernel update command (%d) does not exist",
+ cmd);
+ return -1;
+ }
+
+ _netlink_nexthop_debug(cmd, id);
+
+ return netlink_talk_info(netlink_talk_filter, &req.n,
+ dplane_ctx_get_ns(ctx), 0);
+}
+
+/**
+ * kernel_nexthop_update() - Update/delete a nexthop from the kernel
+ *
+ * @ctx: Dataplane context
+ *
+ * Return: Dataplane result flag
+ */
+enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx)
+{
+ int cmd = 0;
+ int ret = 0;
+
+ switch (dplane_ctx_get_op(ctx)) {
+ case DPLANE_OP_NH_DELETE:
+ cmd = RTM_DELNEXTHOP;
+ break;
+ case DPLANE_OP_NH_INSTALL:
+ case DPLANE_OP_NH_UPDATE:
+ cmd = RTM_NEWNEXTHOP;
+ break;
+ case DPLANE_OP_ROUTE_INSTALL:
+ case DPLANE_OP_ROUTE_UPDATE:
+ case DPLANE_OP_ROUTE_DELETE:
+ case DPLANE_OP_ROUTE_NOTIFY:
+ case DPLANE_OP_LSP_INSTALL:
+ case DPLANE_OP_LSP_UPDATE:
+ case DPLANE_OP_LSP_DELETE:
+ case DPLANE_OP_LSP_NOTIFY:
+ case DPLANE_OP_PW_INSTALL:
+ case DPLANE_OP_PW_UNINSTALL:
+ case DPLANE_OP_SYS_ROUTE_ADD:
+ case DPLANE_OP_SYS_ROUTE_DELETE:
+ case DPLANE_OP_ADDR_INSTALL:
+ case DPLANE_OP_ADDR_UNINSTALL:
+ case DPLANE_OP_MAC_INSTALL:
+ case DPLANE_OP_MAC_DELETE:
+ case DPLANE_OP_NEIGH_INSTALL:
+ case DPLANE_OP_NEIGH_UPDATE:
+ case DPLANE_OP_NEIGH_DELETE:
+ case DPLANE_OP_VTEP_ADD:
+ case DPLANE_OP_VTEP_DELETE:
+ case DPLANE_OP_NONE:
+ flog_err(
+ EC_ZEBRA_NHG_FIB_UPDATE,
+ "Context received for kernel nexthop update with incorrect OP code (%u)",
+ dplane_ctx_get_op(ctx));
+ return ZEBRA_DPLANE_REQUEST_FAILURE;
+ }
+
+ ret = netlink_nexthop(cmd, ctx);
+
+ return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS
+ : ZEBRA_DPLANE_REQUEST_FAILURE);
+}
+
/*
* Update or delete a prefix from the kernel,
* using info from a dataplane context.
@@ -1919,6 +2226,303 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE);
}
+/**
+ * netlink_nexthop_process_nh() - Parse the gatway/if info from a new nexthop
+ *
+ * @tb: Netlink RTA data
+ * @family: Address family in the nhmsg
+ * @ifp: Interface connected - this should be NULL, we fill it in
+ * @ns_id: Namspace id
+ *
+ * Return: New nexthop
+ */
+static struct nexthop netlink_nexthop_process_nh(struct rtattr **tb,
+ unsigned char family,
+ struct interface **ifp,
+ ns_id_t ns_id)
+{
+ struct nexthop nh = {};
+ void *gate = NULL;
+ enum nexthop_types_t type = 0;
+ int if_index = 0;
+ size_t sz = 0;
+ struct interface *ifp_lookup;
+
+ if_index = *(int *)RTA_DATA(tb[NHA_OIF]);
+
+
+ if (tb[NHA_GATEWAY]) {
+ switch (family) {
+ case AF_INET:
+ type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ sz = 4;
+ break;
+ case AF_INET6:
+ type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ sz = 16;
+ break;
+ default:
+ flog_warn(
+ EC_ZEBRA_BAD_NHG_MESSAGE,
+ "Nexthop gateway with bad address family (%d) received from kernel",
+ family);
+ return nh;
+ }
+ gate = RTA_DATA(tb[NHA_GATEWAY]);
+ } else
+ type = NEXTHOP_TYPE_IFINDEX;
+
+ if (type)
+ nh.type = type;
+
+ if (gate)
+ memcpy(&(nh.gate), gate, sz);
+
+ if (if_index)
+ nh.ifindex = if_index;
+
+ ifp_lookup =
+ if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), nh.ifindex);
+
+ if (ifp)
+ *ifp = ifp_lookup;
+ if (ifp_lookup)
+ nh.vrf_id = ifp_lookup->vrf_id;
+ else {
+ flog_warn(
+ EC_ZEBRA_UNKNOWN_INTERFACE,
+ "%s: Unknown nexthop interface %u received, defaulting to VRF_DEFAULT",
+ __PRETTY_FUNCTION__, nh.ifindex);
+
+ nh.vrf_id = VRF_DEFAULT;
+ }
+
+ if (tb[NHA_ENCAP] && tb[NHA_ENCAP_TYPE]) {
+ uint16_t encap_type = *(uint16_t *)RTA_DATA(tb[NHA_ENCAP_TYPE]);
+ int num_labels = 0;
+
+ mpls_label_t labels[MPLS_MAX_LABELS] = {0};
+
+ if (encap_type == LWTUNNEL_ENCAP_MPLS)
+ num_labels = parse_encap_mpls(tb[NHA_ENCAP], labels);
+
+ if (num_labels)
+ nexthop_add_labels(&nh, ZEBRA_LSP_STATIC, num_labels,
+ labels);
+ }
+
+ return nh;
+}
+
+static int netlink_nexthop_process_group(struct rtattr **tb,
+ struct nh_grp *z_grp, int z_grp_size)
+{
+ uint8_t count = 0;
+ /* linux/nexthop.h group struct */
+ struct nexthop_grp *n_grp = NULL;
+
+ n_grp = (struct nexthop_grp *)RTA_DATA(tb[NHA_GROUP]);
+ count = (RTA_PAYLOAD(tb[NHA_GROUP]) / sizeof(*n_grp));
+
+ if (!count || (count * sizeof(*n_grp)) != RTA_PAYLOAD(tb[NHA_GROUP])) {
+ flog_warn(EC_ZEBRA_BAD_NHG_MESSAGE,
+ "Invalid nexthop group received from the kernel");
+ return count;
+ }
+
+#if 0
+ // TODO: Need type for something?
+ zlog_debug("Nexthop group type: %d",
+ *((uint16_t *)RTA_DATA(tb[NHA_GROUP_TYPE])));
+
+#endif
+
+ for (int i = 0; ((i < count) && (i < z_grp_size)); i++) {
+ z_grp[i].id = n_grp[i].id;
+ z_grp[i].weight = n_grp[i].weight;
+ }
+ return count;
+}
+
+/**
+ * netlink_nexthop_change() - Read in change about nexthops from the kernel
+ *
+ * @h: Netlink message header
+ * @ns_id: Namspace id
+ * @startup: Are we reading under startup conditions?
+ *
+ * Return: Result status
+ */
+int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
+{
+ int len;
+ /* nexthop group id */
+ uint32_t id;
+ unsigned char family;
+ int type;
+ afi_t afi = AFI_UNSPEC;
+ vrf_id_t vrf_id = 0;
+ struct interface *ifp = NULL;
+ struct nhmsg *nhm = NULL;
+ struct nexthop nh = {};
+ struct nh_grp grp[MULTIPATH_NUM] = {};
+ /* Count of nexthops in group array */
+ uint8_t grp_count = 0;
+ struct rtattr *tb[NHA_MAX + 1] = {};
+
+ nhm = NLMSG_DATA(h);
+
+ if (startup && h->nlmsg_type != RTM_NEWNEXTHOP)
+ return 0;
+
+ len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct nhmsg));
+ if (len < 0) {
+ zlog_warn(
+ "%s: Message received from netlink is of a broken size %d %zu",
+ __PRETTY_FUNCTION__, h->nlmsg_len,
+ (size_t)NLMSG_LENGTH(sizeof(struct nhmsg)));
+ return -1;
+ }
+
+ netlink_parse_rtattr(tb, NHA_MAX, RTM_NHA(nhm), len);
+
+
+ if (!tb[NHA_ID]) {
+ flog_warn(
+ EC_ZEBRA_BAD_NHG_MESSAGE,
+ "Nexthop group without an ID received from the kernel");
+ return -1;
+ }
+
+ /* We use the ID key'd nhg table for kernel updates */
+ id = *((uint32_t *)RTA_DATA(tb[NHA_ID]));
+
+ family = nhm->nh_family;
+ afi = family2afi(family);
+
+ type = proto2zebra(nhm->nh_protocol, 0, true);
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s ID (%u) %s NS %u",
+ nl_msg_type_to_str(h->nlmsg_type), id,
+ nl_family_to_str(family), ns_id);
+
+
+ if (h->nlmsg_type == RTM_NEWNEXTHOP) {
+ if (tb[NHA_GROUP]) {
+ /**
+ * If this is a group message its only going to have
+ * an array of nexthop IDs associated with it
+ */
+ grp_count = netlink_nexthop_process_group(
+ tb, grp, array_size(grp));
+ } else {
+ if (tb[NHA_BLACKHOLE]) {
+ /**
+ * This nexthop is just for blackhole-ing
+ * traffic, it should not have an OIF, GATEWAY,
+ * or ENCAP
+ */
+ nh.type = NEXTHOP_TYPE_BLACKHOLE;
+ nh.bh_type = BLACKHOLE_UNSPEC;
+ } else if (tb[NHA_OIF])
+ /**
+ * This is a true new nexthop, so we need
+ * to parse the gateway and device info
+ */
+ nh = netlink_nexthop_process_nh(tb, family,
+ &ifp, ns_id);
+ else {
+
+ flog_warn(
+ EC_ZEBRA_BAD_NHG_MESSAGE,
+ "Invalid Nexthop message received from the kernel with ID (%u)",
+ id);
+ return -1;
+ }
+ SET_FLAG(nh.flags, NEXTHOP_FLAG_ACTIVE);
+ if (nhm->nh_flags & RTNH_F_ONLINK)
+ SET_FLAG(nh.flags, NEXTHOP_FLAG_ONLINK);
+ vrf_id = nh.vrf_id;
+ }
+
+ if (zebra_nhg_kernel_find(id, &nh, grp, grp_count, vrf_id, afi,
+ type, startup))
+ return -1;
+
+ } else if (h->nlmsg_type == RTM_DELNEXTHOP)
+ zebra_nhg_kernel_del(id);
+
+ return 0;
+}
+
+#if 0 /* Force off kernel nexthop group installs for now */
+/**
+ * netlink_request_nexthop() - Request nextop information from the kernel
+ * @zns: Zebra namespace
+ * @family: AF_* netlink family
+ * @type: RTM_* route type
+ *
+ * Return: Result status
+ */
+static int netlink_request_nexthop(struct zebra_ns *zns, int family, int type)
+{
+ struct {
+ struct nlmsghdr n;
+ struct nhmsg nhm;
+ } req;
+
+ /* Form the request, specifying filter (rtattr) if needed. */
+ memset(&req, 0, sizeof(req));
+ req.n.nlmsg_type = type;
+ req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg));
+ req.nhm.nh_family = family;
+
+ return netlink_request(&zns->netlink_cmd, &req.n);
+}
+
+
+/**
+ * netlink_nexthop_read() - Nexthop read function using netlink interface
+ *
+ * @zns: Zebra name space
+ *
+ * Return: Result status
+ * Only called at bootstrap time.
+ */
+int netlink_nexthop_read(struct zebra_ns *zns)
+{
+ int ret;
+ struct zebra_dplane_info dp_info;
+
+ zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/);
+
+ /* Get nexthop objects */
+ ret = netlink_request_nexthop(zns, AF_UNSPEC, RTM_GETNEXTHOP);
+ if (ret < 0)
+ return ret;
+ ret = netlink_parse_info(netlink_nexthop_change, &zns->netlink_cmd,
+ &dp_info, 0, 1);
+
+ if (!ret)
+ /* If we succesfully read in nexthop objects,
+ * this kernel must support them.
+ */
+ supports_nh = true;
+ else if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("Nexthop objects not supported on this kernel");
+
+ return ret;
+}
+#else
+int netlink_nexthop_read(struct zebra_ns *zns)
+{
+ return 0;
+}
+#endif
+
+
int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla,
int llalen, ns_id_t ns_id)
{
@@ -1951,7 +2555,7 @@ static int netlink_vxlan_flood_update_ctx(const struct zebra_dplane_ctx *ctx,
req.n.nlmsg_type = cmd;
req.ndm.ndm_family = PF_BRIDGE;
req.ndm.ndm_state = NUD_NOARP | NUD_PERMANENT;
- req.ndm.ndm_flags |= NTF_SELF; // Handle by "self", not "master"
+ req.ndm.ndm_flags |= NTF_SELF; /* Handle by "self", not "master" */
addattr_l(&req.n, sizeof(req),
diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h
index 29e0152bb2..2b4b145149 100644
--- a/zebra/rt_netlink.h
+++ b/zebra/rt_netlink.h
@@ -69,6 +69,10 @@ extern int netlink_mpls_multipath(int cmd, struct zebra_dplane_ctx *ctx);
extern int netlink_route_change(struct nlmsghdr *h, ns_id_t ns_id, int startup);
extern int netlink_route_read(struct zebra_ns *zns);
+extern int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id,
+ int startup);
+extern int netlink_nexthop_read(struct zebra_ns *zns);
+
extern int netlink_neigh_change(struct nlmsghdr *h, ns_id_t ns_id);
extern int netlink_macfdb_read(struct zebra_ns *zns);
extern int netlink_macfdb_read_for_bridge(struct zebra_ns *zns,
diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c
index 981ef7a889..73b3dd0b40 100644
--- a/zebra/rt_socket.c
+++ b/zebra/rt_socket.c
@@ -364,6 +364,11 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
return res;
}
+enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx)
+{
+ return ZEBRA_DPLANE_REQUEST_SUCCESS;
+}
+
int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla,
int llalen, ns_id_t ns_id)
{
diff --git a/zebra/rtread_getmsg.c b/zebra/rtread_getmsg.c
index 725bb63a0d..3ba5d6ee73 100644
--- a/zebra/rtread_getmsg.c
+++ b/zebra/rtread_getmsg.c
@@ -102,7 +102,7 @@ static void handle_route_entry(mib2_ipRouteEntry_t *routeEntry)
nh.gate.ipv4.s_addr = routeEntry->ipRouteNextHop;
rib_add(AFI_IP, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0,
- zebra_flags, &prefix, NULL, &nh, 0, 0, 0, 0, 0);
+ zebra_flags, &prefix, NULL, &nh, 0, 0, 0, 0, 0, 0);
}
void route_read(struct zebra_ns *zns)
diff --git a/zebra/subdir.am b/zebra/subdir.am
index 25040a2717..28847ce09b 100644
--- a/zebra/subdir.am
+++ b/zebra/subdir.am
@@ -137,6 +137,7 @@ noinst_HEADERS += \
zebra/zebra_mpls.h \
zebra/zebra_mroute.h \
zebra/zebra_nhg.h \
+ zebra/zebra_nhg_private.h \
zebra/zebra_ns.h \
zebra/zebra_pbr.h \
zebra/zebra_ptm.h \
diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c
index e61e68b7fe..d6ade783cf 100644
--- a/zebra/zapi_msg.c
+++ b/zebra/zapi_msg.c
@@ -522,7 +522,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client,
struct zapi_route api;
struct zapi_nexthop *api_nh;
struct nexthop *nexthop;
- int count = 0;
+ uint8_t count = 0;
afi_t afi;
size_t stream_size =
MAX(ZEBRA_MAX_PACKET_SIZ, sizeof(struct zapi_route));
@@ -559,12 +559,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client,
memcpy(&api.src_prefix, src_p, sizeof(api.src_prefix));
}
- /* Nexthops. */
- if (re->nexthop_active_num) {
- SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
- api.nexthop_num = re->nexthop_active_num;
- }
- for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) {
+ for (nexthop = re->ng->nexthop; nexthop; nexthop = nexthop->next) {
if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
continue;
@@ -595,6 +590,12 @@ int zsend_redistribute_route(int cmd, struct zserv *client,
count++;
}
+ /* Nexthops. */
+ if (count) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
+ api.nexthop_num = count;
+ }
+
/* Attributes. */
SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE);
api.distance = re->distance;
@@ -665,7 +666,8 @@ static int zsend_ipv4_nexthop_lookup_mrib(struct zserv *client,
* nexthop we are looking up. Therefore, we will just iterate
* over the top chain of nexthops.
*/
- for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next)
+ for (nexthop = re->ng->nexthop; nexthop;
+ nexthop = nexthop->next)
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
num += zserv_encode_nexthop(s, nexthop);
@@ -1422,6 +1424,8 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
re->flags = api.flags;
re->uptime = monotime(NULL);
re->vrf_id = vrf_id;
+ re->ng = nexthop_group_new();
+
if (api.tableid)
re->table = api.tableid;
else
@@ -1433,6 +1437,8 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
"%s: received a route without nexthops for prefix %pFX from client %s",
__func__, &api.prefix,
zebra_route_string(client->proto));
+
+ nexthop_group_delete(&re->ng);
XFREE(MTYPE_RE, re);
return;
}
@@ -1531,7 +1537,7 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
EC_ZEBRA_NEXTHOP_CREATION_FAILED,
"%s: Nexthops Specified: %d but we failed to properly create one",
__PRETTY_FUNCTION__, api.nexthop_num);
- nexthops_free(re->ng.nexthop);
+ nexthop_group_delete(&re->ng);
XFREE(MTYPE_RE, re);
return;
}
@@ -1573,7 +1579,7 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
flog_warn(EC_ZEBRA_RX_SRCDEST_WRONG_AFI,
"%s: Received SRC Prefix but afi is not v6",
__PRETTY_FUNCTION__);
- nexthops_free(re->ng.nexthop);
+ nexthop_group_delete(&re->ng);
XFREE(MTYPE_RE, re);
return;
}
@@ -1627,7 +1633,7 @@ static void zread_route_del(ZAPI_HANDLER_ARGS)
table_id = zvrf->table_id;
rib_delete(afi, api.safi, zvrf_id(zvrf), api.type, api.instance,
- api.flags, &api.prefix, src_p, NULL, table_id, api.metric,
+ api.flags, &api.prefix, src_p, NULL, 0, table_id, api.metric,
api.distance, false);
/* Stats */
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index bf343e06e5..a88b0a38da 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -67,6 +67,20 @@ const uint32_t DPLANE_DEFAULT_NEW_WORK = 100;
#endif /* DPLANE_DEBUG */
/*
+ * Nexthop information captured for nexthop/nexthop group updates
+ */
+struct dplane_nexthop_info {
+ uint32_t id;
+ afi_t afi;
+ vrf_id_t vrf_id;
+ int type;
+
+ struct nexthop_group ng;
+ struct nh_grp nh_grp[MULTIPATH_NUM];
+ uint8_t nh_grp_count;
+};
+
+/*
* Route information captured for route updates.
*/
struct dplane_route_info {
@@ -95,6 +109,9 @@ struct dplane_route_info {
uint32_t zd_mtu;
uint32_t zd_nexthop_mtu;
+ /* Nexthop hash entry info */
+ struct dplane_nexthop_info nhe;
+
/* Nexthops */
struct nexthop_group zd_ng;
@@ -321,6 +338,9 @@ static struct zebra_dplane_globals {
_Atomic uint32_t dg_route_errors;
_Atomic uint32_t dg_other_errors;
+ _Atomic uint32_t dg_nexthops_in;
+ _Atomic uint32_t dg_nexthop_errors;
+
_Atomic uint32_t dg_lsps_in;
_Atomic uint32_t dg_lsp_errors;
@@ -461,6 +481,18 @@ static void dplane_ctx_free(struct zebra_dplane_ctx **pctx)
break;
+ case DPLANE_OP_NH_INSTALL:
+ case DPLANE_OP_NH_UPDATE:
+ case DPLANE_OP_NH_DELETE: {
+ if ((*pctx)->u.rinfo.nhe.ng.nexthop) {
+ /* This deals with recursive nexthops too */
+ nexthops_free((*pctx)->u.rinfo.nhe.ng.nexthop);
+
+ (*pctx)->u.rinfo.nhe.ng.nexthop = NULL;
+ }
+ break;
+ }
+
case DPLANE_OP_LSP_INSTALL:
case DPLANE_OP_LSP_UPDATE:
case DPLANE_OP_LSP_DELETE:
@@ -638,6 +670,17 @@ const char *dplane_op2str(enum dplane_op_e op)
ret = "ROUTE_NOTIFY";
break;
+ /* Nexthop update */
+ case DPLANE_OP_NH_INSTALL:
+ ret = "NH_INSTALL";
+ break;
+ case DPLANE_OP_NH_UPDATE:
+ ret = "NH_UPDATE";
+ break;
+ case DPLANE_OP_NH_DELETE:
+ ret = "NH_DELETE";
+ break;
+
case DPLANE_OP_LSP_INSTALL:
ret = "LSP_INSTALL";
break;
@@ -1015,6 +1058,51 @@ const struct zebra_dplane_info *dplane_ctx_get_ns(
return &(ctx->zd_ns_info);
}
+/* Accessors for nexthop information */
+uint32_t dplane_ctx_get_nhe_id(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return ctx->u.rinfo.nhe.id;
+}
+
+afi_t dplane_ctx_get_nhe_afi(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return ctx->u.rinfo.nhe.afi;
+}
+
+vrf_id_t dplane_ctx_get_nhe_vrf_id(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return ctx->u.rinfo.nhe.vrf_id;
+}
+
+int dplane_ctx_get_nhe_type(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return ctx->u.rinfo.nhe.type;
+}
+
+const struct nexthop_group *
+dplane_ctx_get_nhe_ng(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return &(ctx->u.rinfo.nhe.ng);
+}
+
+const struct nh_grp *
+dplane_ctx_get_nhe_nh_grp(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return ctx->u.rinfo.nhe.nh_grp;
+}
+
+uint8_t dplane_ctx_get_nhe_nh_grp_count(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return ctx->u.rinfo.nhe.nh_grp_count;
+}
+
/* Accessors for LSP information */
mpls_label_t dplane_ctx_get_in_label(const struct zebra_dplane_ctx *ctx)
@@ -1419,7 +1507,7 @@ static int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx,
ctx->u.rinfo.zd_safi = info->safi;
/* Copy nexthops; recursive info is included too */
- copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop), re->ng.nexthop, NULL);
+ copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop), re->ng->nexthop, NULL);
/* Ensure that the dplane's nexthops flags are clear. */
for (ALL_NEXTHOPS(ctx->u.rinfo.zd_ng, nexthop))
@@ -1437,6 +1525,29 @@ static int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx,
zns = zvrf->zns;
dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_ROUTE_UPDATE));
+#ifdef HAVE_NETLINK
+ if (re->nhe_id) {
+ struct nhg_hash_entry *nhe =
+ zebra_nhg_resolve(zebra_nhg_lookup_id(re->nhe_id));
+
+ ctx->u.rinfo.nhe.id = nhe->id;
+ /*
+ * Check if the nhe is installed/queued before doing anything
+ * with this route.
+ *
+ * If its a delete we only use the prefix anyway, so this only
+ * matters for INSTALL/UPDATE.
+ */
+ if (((op == DPLANE_OP_ROUTE_INSTALL)
+ || (op == DPLANE_OP_ROUTE_UPDATE))
+ && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED)
+ && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED)) {
+ ret = ENOENT;
+ goto done;
+ }
+ }
+#endif /* HAVE_NETLINK */
+
/* Trying out the sequence number idea, so we can try to detect
* when a result is stale.
*/
@@ -1449,6 +1560,64 @@ done:
return ret;
}
+/**
+ * dplane_ctx_nexthop_init() - Initialize a context block for a nexthop update
+ *
+ * @ctx: Dataplane context to init
+ * @op: Operation being performed
+ * @nhe: Nexthop group hash entry
+ *
+ * Return: Result status
+ */
+static int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx,
+ enum dplane_op_e op,
+ struct nhg_hash_entry *nhe)
+{
+ struct zebra_vrf *zvrf = NULL;
+ struct zebra_ns *zns = NULL;
+
+ int ret = EINVAL;
+
+ if (!ctx || !nhe)
+ goto done;
+
+ ctx->zd_op = op;
+ ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
+
+ /* Copy over nhe info */
+ ctx->u.rinfo.nhe.id = nhe->id;
+ ctx->u.rinfo.nhe.afi = nhe->afi;
+ ctx->u.rinfo.nhe.vrf_id = nhe->vrf_id;
+ ctx->u.rinfo.nhe.type = nhe->type;
+
+ nexthop_group_copy(&(ctx->u.rinfo.nhe.ng), nhe->nhg);
+
+ /* If its a group, convert it to a grp array of ids */
+ if (!zebra_nhg_depends_is_empty(nhe)
+ && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE))
+ ctx->u.rinfo.nhe.nh_grp_count = zebra_nhg_nhe2grp(
+ ctx->u.rinfo.nhe.nh_grp, nhe, MULTIPATH_NUM);
+
+ zvrf = vrf_info_lookup(nhe->vrf_id);
+
+ /*
+ * Fallback to default namespace if the vrf got ripped out from under
+ * us.
+ */
+ zns = zvrf ? zvrf->zns : zebra_ns_lookup(NS_DEFAULT);
+
+ /*
+ * TODO: Might not need to mark this as an update, since
+ * it probably won't require two messages
+ */
+ dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_NH_UPDATE));
+
+ ret = AOK;
+
+done:
+ return ret;
+}
+
/*
* Capture information for an LSP update in a dplane context.
*/
@@ -1577,7 +1746,7 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx,
if (re)
copy_nexthops(&(ctx->u.pw.nhg.nexthop),
- re->ng.nexthop, NULL);
+ re->ng->nexthop, NULL);
route_unlock_node(rn);
}
@@ -1673,7 +1842,7 @@ dplane_route_update_internal(struct route_node *rn,
* We'll need these to do per-nexthop deletes.
*/
copy_nexthops(&(ctx->u.rinfo.zd_old_ng.nexthop),
- old_re->ng.nexthop, NULL);
+ old_re->ng->nexthop, NULL);
#endif /* !HAVE_NETLINK */
}
@@ -1688,7 +1857,53 @@ dplane_route_update_internal(struct route_node *rn,
if (ret == AOK)
result = ZEBRA_DPLANE_REQUEST_QUEUED;
else {
- atomic_fetch_add_explicit(&zdplane_info.dg_route_errors, 1,
+ if (ret == ENOENT)
+ result = ZEBRA_DPLANE_REQUEST_SUCCESS;
+ else
+ atomic_fetch_add_explicit(&zdplane_info.dg_route_errors,
+ 1, memory_order_relaxed);
+ if (ctx)
+ dplane_ctx_free(&ctx);
+ }
+
+ return result;
+}
+
+/**
+ * dplane_nexthop_update_internal() - Helper for enqueuing nexthop changes
+ *
+ * @nhe: Nexthop group hash entry where the change occured
+ * @op: The operation to be enqued
+ *
+ * Return: Result of the change
+ */
+static enum zebra_dplane_result
+dplane_nexthop_update_internal(struct nhg_hash_entry *nhe, enum dplane_op_e op)
+{
+ enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
+ int ret = EINVAL;
+ struct zebra_dplane_ctx *ctx = NULL;
+
+ /* Obtain context block */
+ ctx = dplane_ctx_alloc();
+ if (!ctx) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = dplane_ctx_nexthop_init(ctx, op, nhe);
+ if (ret == AOK)
+ ret = dplane_update_enqueue(ctx);
+
+done:
+ /* Update counter */
+ atomic_fetch_add_explicit(&zdplane_info.dg_nexthops_in, 1,
+ memory_order_relaxed);
+
+ if (ret == AOK)
+ result = ZEBRA_DPLANE_REQUEST_QUEUED;
+ else {
+ atomic_fetch_add_explicit(&zdplane_info.dg_nexthop_errors, 1,
memory_order_relaxed);
if (ctx)
dplane_ctx_free(&ctx);
@@ -1853,6 +2068,45 @@ done:
}
/*
+ * Enqueue a nexthop add for the dataplane.
+ */
+enum zebra_dplane_result dplane_nexthop_add(struct nhg_hash_entry *nhe)
+{
+ enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
+
+ if (nhe)
+ ret = dplane_nexthop_update_internal(nhe, DPLANE_OP_NH_INSTALL);
+ return ret;
+}
+
+/*
+ * Enqueue a nexthop update for the dataplane.
+ *
+ * Might not need this func since zebra's nexthop objects should be immutable?
+ */
+enum zebra_dplane_result dplane_nexthop_update(struct nhg_hash_entry *nhe)
+{
+ enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
+
+ if (nhe)
+ ret = dplane_nexthop_update_internal(nhe, DPLANE_OP_NH_UPDATE);
+ return ret;
+}
+
+/*
+ * Enqueue a nexthop removal for the dataplane.
+ */
+enum zebra_dplane_result dplane_nexthop_delete(struct nhg_hash_entry *nhe)
+{
+ enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
+
+ if (nhe)
+ ret = dplane_nexthop_update_internal(nhe, DPLANE_OP_NH_DELETE);
+
+ return ret;
+}
+
+/*
* Enqueue LSP add for the dataplane.
*/
enum zebra_dplane_result dplane_lsp_add(zebra_lsp_t *lsp)
@@ -2873,6 +3127,33 @@ kernel_dplane_address_update(struct zebra_dplane_ctx *ctx)
return res;
}
+/**
+ * kernel_dplane_nexthop_update() - Handler for kernel nexthop updates
+ *
+ * @ctx: Dataplane context
+ *
+ * Return: Dataplane result flag
+ */
+static enum zebra_dplane_result
+kernel_dplane_nexthop_update(struct zebra_dplane_ctx *ctx)
+{
+ enum zebra_dplane_result res;
+
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
+ zlog_debug("ID (%u) Dplane nexthop update ctx %p op %s",
+ dplane_ctx_get_nhe_id(ctx), ctx,
+ dplane_op2str(dplane_ctx_get_op(ctx)));
+ }
+
+ res = kernel_nexthop_update(ctx);
+
+ if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
+ atomic_fetch_add_explicit(&zdplane_info.dg_nexthop_errors, 1,
+ memory_order_relaxed);
+
+ return res;
+}
+
/*
* Handler for kernel-facing EVPN MAC address updates
*/
@@ -2967,6 +3248,12 @@ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
res = kernel_dplane_route_update(ctx);
break;
+ case DPLANE_OP_NH_INSTALL:
+ case DPLANE_OP_NH_UPDATE:
+ case DPLANE_OP_NH_DELETE:
+ res = kernel_dplane_nexthop_update(ctx);
+ break;
+
case DPLANE_OP_LSP_INSTALL:
case DPLANE_OP_LSP_UPDATE:
case DPLANE_OP_LSP_DELETE:
diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h
index be945632c1..fede3bfcca 100644
--- a/zebra/zebra_dplane.h
+++ b/zebra/zebra_dplane.h
@@ -30,6 +30,7 @@
#include "zebra/rib.h"
#include "zebra/zserv.h"
#include "zebra/zebra_mpls.h"
+#include "zebra/zebra_nhg.h"
#ifdef __cplusplus
extern "C" {
@@ -108,6 +109,11 @@ enum dplane_op_e {
DPLANE_OP_ROUTE_DELETE,
DPLANE_OP_ROUTE_NOTIFY,
+ /* Nexthop update */
+ DPLANE_OP_NH_INSTALL,
+ DPLANE_OP_NH_UPDATE,
+ DPLANE_OP_NH_DELETE,
+
/* LSP update */
DPLANE_OP_LSP_INSTALL,
DPLANE_OP_LSP_UPDATE,
@@ -269,6 +275,17 @@ const struct nexthop_group *dplane_ctx_get_ng(
const struct nexthop_group *dplane_ctx_get_old_ng(
const struct zebra_dplane_ctx *ctx);
+/* Accessors for nexthop information */
+uint32_t dplane_ctx_get_nhe_id(const struct zebra_dplane_ctx *ctx);
+afi_t dplane_ctx_get_nhe_afi(const struct zebra_dplane_ctx *ctx);
+vrf_id_t dplane_ctx_get_nhe_vrf_id(const struct zebra_dplane_ctx *ctx);
+int dplane_ctx_get_nhe_type(const struct zebra_dplane_ctx *ctx);
+const struct nexthop_group *
+dplane_ctx_get_nhe_ng(const struct zebra_dplane_ctx *ctx);
+const struct nh_grp *
+dplane_ctx_get_nhe_nh_grp(const struct zebra_dplane_ctx *ctx);
+uint8_t dplane_ctx_get_nhe_nh_grp_count(const struct zebra_dplane_ctx *ctx);
+
/* Accessors for LSP information */
mpls_label_t dplane_ctx_get_in_label(const struct zebra_dplane_ctx *ctx);
void dplane_ctx_set_in_label(struct zebra_dplane_ctx *ctx,
@@ -373,6 +390,16 @@ enum zebra_dplane_result dplane_route_notif_update(
enum dplane_op_e op,
struct zebra_dplane_ctx *ctx);
+
+/* Forward ref of nhg_hash_entry */
+struct nhg_hash_entry;
+/*
+ * Enqueue a nexthop change operation for the dataplane.
+ */
+enum zebra_dplane_result dplane_nexthop_add(struct nhg_hash_entry *nhe);
+enum zebra_dplane_result dplane_nexthop_update(struct nhg_hash_entry *nhe);
+enum zebra_dplane_result dplane_nexthop_delete(struct nhg_hash_entry *nhe);
+
/*
* Enqueue LSP change operations for the dataplane.
*/
diff --git a/zebra/zebra_errors.c b/zebra/zebra_errors.c
index a7e5147af3..5a0905d591 100644
--- a/zebra/zebra_errors.c
+++ b/zebra/zebra_errors.c
@@ -283,6 +283,39 @@ static struct log_ref ferr_zebra_err[] = {
.description = "Zebra received an event from inotify, but failed to read what it was.",
.suggestion = "Notify a developer.",
},
+ {
+ .code = EC_ZEBRA_NHG_TABLE_INSERT_FAILED,
+ .title =
+ "Nexthop Group Hash Table Insert Failure",
+ .description =
+ "Zebra failed in inserting a Nexthop Group into its hash tables.",
+ .suggestion =
+ "Check to see if the entry already exists or if the netlink message was parsed incorrectly."
+ },
+ {
+ .code = EC_ZEBRA_NHG_SYNC,
+ .title =
+ "Zebra's Nexthop Groups are out of sync",
+ .description =
+ "Zebra's nexthop group tables are out of sync with the nexthop groups in the fib.",
+ .suggestion =
+ "Check the current status of the kernels nexthop groups and compare it to Zebra's."
+ },
+ {
+ .code = EC_ZEBRA_NHG_FIB_UPDATE,
+ .title =
+ "Zebra failed updating the fib with Nexthop Group",
+ .description =
+ "Zebra was not able to successfully install a new nexthop group into the fib",
+ .suggestion =
+ "Check to see if the nexthop group on the route you tried to install is valid."
+ },
+ {
+ .code = EC_ZEBRA_IF_LOOKUP_FAILED,
+ .title = "Zebra interface lookup failed",
+ .description = "Zebra attempted to look up a interface for a particular vrf_id and interface index, but didn't find anything.",
+ .suggestion = "If you entered a command to trigger this error, make sure you entered the arguments correctly. Check your config file for any potential errors. If these look correct, seek help.",
+ },
/* Warnings */
{
.code = EC_ZEBRAING_LM_PROTO_MISMATCH,
@@ -729,6 +762,24 @@ static struct log_ref ferr_zebra_err[] = {
"Check network topology to detect duplicate host IP for correctness.",
},
{
+ .code = EC_ZEBRA_BAD_NHG_MESSAGE,
+ .title =
+ "Bad Nexthop Group Message",
+ .description =
+ "Zebra received Nexthop Group message from the kernel that it cannot process.",
+ .suggestion =
+ "Check the kernel's link states and routing table to see how it matches ours."
+ },
+ {
+ .code = EC_ZEBRA_DUPLICATE_NHG_MESSAGE,
+ .title =
+ "Duplicate Nexthop Group Message",
+ .description =
+ "Zebra received Nexthop Group message from the kernel that it is identical to one it/we already have but with a different ID.",
+ .suggestion =
+ "See if the nexthop you are trying to add is already present in the fib."
+ },
+ {
.code = END_FERR,
}
};
diff --git a/zebra/zebra_errors.h b/zebra/zebra_errors.h
index 222055dd81..f9ccc2db28 100644
--- a/zebra/zebra_errors.h
+++ b/zebra/zebra_errors.h
@@ -72,6 +72,10 @@ enum zebra_log_refs {
EC_ZEBRA_VNI_DEL_FAILED,
EC_ZEBRA_VTEP_ADD_FAILED,
EC_ZEBRA_VNI_ADD_FAILED,
+ EC_ZEBRA_NHG_TABLE_INSERT_FAILED,
+ EC_ZEBRA_NHG_SYNC,
+ EC_ZEBRA_NHG_FIB_UPDATE,
+ EC_ZEBRA_IF_LOOKUP_FAILED,
/* warnings */
EC_ZEBRA_NS_NOTIFY_READ,
EC_ZEBRAING_LM_PROTO_MISMATCH,
@@ -125,6 +129,8 @@ enum zebra_log_refs {
EC_ZEBRA_DUP_MAC_DETECTED,
EC_ZEBRA_DUP_IP_INHERIT_DETECTED,
EC_ZEBRA_DUP_IP_DETECTED,
+ EC_ZEBRA_BAD_NHG_MESSAGE,
+ EC_ZEBRA_DUPLICATE_NHG_MESSAGE,
};
void zebra_error_init(void);
diff --git a/zebra/zebra_fpm_dt.c b/zebra/zebra_fpm_dt.c
index e87fa0ad71..debcf60ee5 100644
--- a/zebra/zebra_fpm_dt.c
+++ b/zebra/zebra_fpm_dt.c
@@ -90,7 +90,7 @@ static int zfpm_dt_find_route(rib_dest_t **dest_p, struct route_entry **re_p)
if (!re)
continue;
- if (re->nexthop_active_num <= 0)
+ if (nexthop_group_active_nexthop_num(re->ng) == 0)
continue;
*dest_p = dest;
diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c
index f347d3955c..b54d8fbc12 100644
--- a/zebra/zebra_fpm_netlink.c
+++ b/zebra/zebra_fpm_netlink.c
@@ -314,7 +314,7 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd,
ri->rtm_type = RTN_UNICAST;
ri->metric = &re->metric;
- for (ALL_NEXTHOPS(re->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
if (ri->num_nhs >= zrouter.multipath_num)
break;
diff --git a/zebra/zebra_fpm_protobuf.c b/zebra/zebra_fpm_protobuf.c
index 3054b8a34d..a11517ab8b 100644
--- a/zebra/zebra_fpm_protobuf.c
+++ b/zebra/zebra_fpm_protobuf.c
@@ -173,7 +173,7 @@ static Fpm__AddRoute *create_add_route_message(qpb_allocator_t *allocator,
* Figure out the set of nexthops to be added to the message.
*/
num_nhs = 0;
- for (ALL_NEXTHOPS(re->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
if (num_nhs >= zrouter.multipath_num)
break;
diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c
index 8088ec1bfe..ef1bd02608 100644
--- a/zebra/zebra_mpls.c
+++ b/zebra/zebra_mpls.c
@@ -185,7 +185,7 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label,
* the label advertised by the recursive nexthop (plus we don't have the
* logic yet to push multiple labels).
*/
- for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) {
+ for (nexthop = re->ng->nexthop; nexthop; nexthop = nexthop->next) {
/* Skip inactive and recursive entries. */
if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
continue;
@@ -635,7 +635,7 @@ static int nhlfe_nexthop_active_ipv4(zebra_nhlfe_t *nhlfe,
|| !CHECK_FLAG(match->flags, ZEBRA_FLAG_SELECTED))
continue;
- for (match_nh = match->ng.nexthop; match_nh;
+ for (match_nh = match->ng->nexthop; match_nh;
match_nh = match_nh->next) {
if (match->type == ZEBRA_ROUTE_CONNECT
|| nexthop->ifindex == match_nh->ifindex) {
@@ -686,10 +686,10 @@ static int nhlfe_nexthop_active_ipv6(zebra_nhlfe_t *nhlfe,
break;
}
- if (!match || !match->ng.nexthop)
+ if (!match || !match->ng->nexthop)
return 0;
- nexthop->ifindex = match->ng.nexthop->ifindex;
+ nexthop->ifindex = match->ng->nexthop->ifindex;
return 1;
}
@@ -2590,11 +2590,13 @@ int mpls_ftn_update(int add, struct zebra_vrf *zvrf, enum lsp_types_t type,
struct route_node *rn;
struct route_entry *re;
struct nexthop *nexthop;
+ struct nexthop_group new_grp = {};
+ struct nhg_hash_entry *nhe = NULL;
bool found;
+ afi_t afi = family2afi(prefix->family);
/* Lookup table. */
- table = zebra_vrf_table(family2afi(prefix->family), SAFI_UNICAST,
- zvrf_id(zvrf));
+ table = zebra_vrf_table(afi, SAFI_UNICAST, zvrf_id(zvrf));
if (!table)
return -1;
@@ -2610,8 +2612,15 @@ int mpls_ftn_update(int add, struct zebra_vrf *zvrf, enum lsp_types_t type,
if (re == NULL)
return -1;
+ /*
+ * Copy over current nexthops into a temporary group.
+ * We can't just change the values here since we are hashing
+ * on labels. We need to create a whole new group
+ */
+ nexthop_group_copy(&new_grp, re->ng);
+
found = false;
- for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) {
+ for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next) {
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
@@ -2625,7 +2634,7 @@ int mpls_ftn_update(int add, struct zebra_vrf *zvrf, enum lsp_types_t type,
continue;
if (!mpls_ftn_update_nexthop(add, nexthop, type,
out_label))
- return 0;
+ break;
found = true;
break;
case NEXTHOP_TYPE_IPV6:
@@ -2640,7 +2649,7 @@ int mpls_ftn_update(int add, struct zebra_vrf *zvrf, enum lsp_types_t type,
continue;
if (!mpls_ftn_update_nexthop(add, nexthop, type,
out_label))
- return 0;
+ break;
found = true;
break;
default:
@@ -2648,14 +2657,19 @@ int mpls_ftn_update(int add, struct zebra_vrf *zvrf, enum lsp_types_t type,
}
}
- if (!found)
- return -1;
+ if (found) {
+ nhe = zebra_nhg_rib_find(0, &new_grp, afi);
- SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
- SET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED);
- rib_queue_add(rn);
+ zebra_nhg_re_update_ref(re, nhe);
- return 0;
+ SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
+ SET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED);
+ rib_queue_add(rn);
+ }
+
+ nexthops_free(new_grp.nexthop);
+
+ return found ? 0 : -1;
}
int mpls_ftn_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type,
@@ -2684,7 +2698,7 @@ int mpls_ftn_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type,
if (re == NULL)
return -1;
- for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next)
+ for (nexthop = re->ng->nexthop; nexthop; nexthop = nexthop->next)
nexthop_del_labels(nexthop);
SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
@@ -2889,7 +2903,12 @@ static void mpls_ftn_uninstall_all(struct zebra_vrf *zvrf,
for (rn = route_top(table); rn; rn = route_next(rn)) {
update = 0;
RNODE_FOREACH_RE (rn, re) {
- for (nexthop = re->ng.nexthop; nexthop;
+ struct nexthop_group new_grp = {};
+ struct nhg_hash_entry *nhe = NULL;
+
+ nexthop_group_copy(&new_grp, re->ng);
+
+ for (nexthop = new_grp.nexthop; nexthop;
nexthop = nexthop->next) {
if (nexthop->nh_label_type != lsp_type)
continue;
@@ -2900,6 +2919,14 @@ static void mpls_ftn_uninstall_all(struct zebra_vrf *zvrf,
ROUTE_ENTRY_LABELS_CHANGED);
update = 1;
}
+
+ if (CHECK_FLAG(re->status,
+ ROUTE_ENTRY_LABELS_CHANGED)) {
+ nhe = zebra_nhg_rib_find(0, &new_grp, afi);
+ zebra_nhg_re_update_ref(re, nhe);
+ }
+
+ nexthops_free(new_grp.nexthop);
}
if (update)
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
index 4e696b39ac..05da25b2b8 100644
--- a/zebra/zebra_nhg.c
+++ b/zebra/zebra_nhg.c
@@ -26,14 +26,1093 @@
#include "lib/nexthop_group_private.h"
#include "lib/routemap.h"
#include "lib/mpls.h"
+#include "lib/jhash.h"
+#include "lib/debug.h"
#include "zebra/connected.h"
#include "zebra/debug.h"
#include "zebra/zebra_router.h"
-#include "zebra/zebra_nhg.h"
+#include "zebra/zebra_nhg_private.h"
#include "zebra/zebra_rnh.h"
#include "zebra/zebra_routemap.h"
+#include "zebra/zebra_memory.h"
+#include "zebra/zserv.h"
#include "zebra/rt.h"
+#include "zebra_errors.h"
+#include "zebra_dplane.h"
+#include "zebra/interface.h"
+
+DEFINE_MTYPE_STATIC(ZEBRA, NHG, "Nexthop Group Entry");
+DEFINE_MTYPE_STATIC(ZEBRA, NHG_CONNECTED, "Nexthop Group Connected");
+DEFINE_MTYPE_STATIC(ZEBRA, NHG_CTX, "Nexthop Group Context");
+
+/* id counter to keep in sync with kernel */
+uint32_t id_counter;
+
+static struct nhg_hash_entry *depends_find(struct nexthop *nh, afi_t afi);
+static void depends_add(struct nhg_connected_tree_head *head,
+ struct nhg_hash_entry *depend);
+static struct nhg_hash_entry *
+depends_find_add(struct nhg_connected_tree_head *head, struct nexthop *nh,
+ afi_t afi);
+static struct nhg_hash_entry *
+depends_find_id_add(struct nhg_connected_tree_head *head, uint32_t id);
+static void depends_decrement_free(struct nhg_connected_tree_head *head);
+
+
+static void nhg_connected_free(struct nhg_connected *dep)
+{
+ XFREE(MTYPE_NHG_CONNECTED, dep);
+}
+
+static struct nhg_connected *nhg_connected_new(struct nhg_hash_entry *nhe)
+{
+ struct nhg_connected *new = NULL;
+
+ new = XCALLOC(MTYPE_NHG_CONNECTED, sizeof(struct nhg_connected));
+ new->nhe = nhe;
+
+ return new;
+}
+
+void nhg_connected_tree_free(struct nhg_connected_tree_head *head)
+{
+ struct nhg_connected *rb_node_dep = NULL;
+
+ if (!nhg_connected_tree_is_empty(head)) {
+ frr_each_safe(nhg_connected_tree, head, rb_node_dep) {
+ nhg_connected_tree_del(head, rb_node_dep);
+ nhg_connected_free(rb_node_dep);
+ }
+ }
+}
+
+bool nhg_connected_tree_is_empty(const struct nhg_connected_tree_head *head)
+{
+ return nhg_connected_tree_count(head) ? false : true;
+}
+
+struct nhg_connected *
+nhg_connected_tree_root(struct nhg_connected_tree_head *head)
+{
+ return nhg_connected_tree_first(head);
+}
+
+void nhg_connected_tree_del_nhe(struct nhg_connected_tree_head *head,
+ struct nhg_hash_entry *depend)
+{
+ struct nhg_connected lookup = {};
+ struct nhg_connected *remove = NULL;
+
+ lookup.nhe = depend;
+
+ /* Lookup to find the element, then remove it */
+ remove = nhg_connected_tree_find(head, &lookup);
+ remove = nhg_connected_tree_del(head, remove);
+
+ if (remove)
+ nhg_connected_free(remove);
+}
+
+void nhg_connected_tree_add_nhe(struct nhg_connected_tree_head *head,
+ struct nhg_hash_entry *depend)
+{
+ struct nhg_connected *new = NULL;
+
+ new = nhg_connected_new(depend);
+
+ if (new)
+ nhg_connected_tree_add(head, new);
+}
+
+static void
+nhg_connected_tree_decrement_ref(struct nhg_connected_tree_head *head)
+{
+ struct nhg_connected *rb_node_dep = NULL;
+
+ frr_each_safe(nhg_connected_tree, head, rb_node_dep) {
+ zebra_nhg_decrement_ref(rb_node_dep->nhe);
+ }
+}
+
+static void
+nhg_connected_tree_increment_ref(struct nhg_connected_tree_head *head)
+{
+ struct nhg_connected *rb_node_dep = NULL;
+
+ frr_each(nhg_connected_tree, head, rb_node_dep) {
+ zebra_nhg_increment_ref(rb_node_dep->nhe);
+ }
+}
+
+struct nhg_hash_entry *zebra_nhg_resolve(struct nhg_hash_entry *nhe)
+{
+ if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE)
+ && !zebra_nhg_depends_is_empty(nhe)) {
+ nhe = nhg_connected_tree_root(&nhe->nhg_depends)->nhe;
+ return zebra_nhg_resolve(nhe);
+ }
+
+ return nhe;
+}
+
+unsigned int zebra_nhg_depends_count(const struct nhg_hash_entry *nhe)
+{
+ return nhg_connected_tree_count(&nhe->nhg_depends);
+}
+
+bool zebra_nhg_depends_is_empty(const struct nhg_hash_entry *nhe)
+{
+ return nhg_connected_tree_is_empty(&nhe->nhg_depends);
+}
+
+static void zebra_nhg_depends_del(struct nhg_hash_entry *from,
+ struct nhg_hash_entry *depend)
+{
+ nhg_connected_tree_del_nhe(&from->nhg_depends, depend);
+}
+
+static void zebra_nhg_depends_init(struct nhg_hash_entry *nhe)
+{
+ nhg_connected_tree_init(&nhe->nhg_depends);
+}
+
+unsigned int zebra_nhg_dependents_count(const struct nhg_hash_entry *nhe)
+{
+ return nhg_connected_tree_count(&nhe->nhg_dependents);
+}
+
+
+bool zebra_nhg_dependents_is_empty(const struct nhg_hash_entry *nhe)
+{
+ return nhg_connected_tree_is_empty(&nhe->nhg_dependents);
+}
+
+static void zebra_nhg_dependents_del(struct nhg_hash_entry *from,
+ struct nhg_hash_entry *dependent)
+{
+ nhg_connected_tree_del_nhe(&from->nhg_dependents, dependent);
+}
+
+static void zebra_nhg_dependents_add(struct nhg_hash_entry *to,
+ struct nhg_hash_entry *dependent)
+{
+ nhg_connected_tree_add_nhe(&to->nhg_dependents, dependent);
+}
+
+static void zebra_nhg_dependents_init(struct nhg_hash_entry *nhe)
+{
+ nhg_connected_tree_init(&nhe->nhg_dependents);
+}
+
+/* Release this nhe from anything depending on it */
+static void zebra_nhg_dependents_release(struct nhg_hash_entry *nhe)
+{
+ struct nhg_connected *rb_node_dep = NULL;
+
+ frr_each_safe(nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep) {
+ zebra_nhg_depends_del(rb_node_dep->nhe, nhe);
+ /* recheck validity of the dependent */
+ zebra_nhg_check_valid(rb_node_dep->nhe);
+ }
+}
+
+/* Release this nhe from anything that it depends on */
+static void zebra_nhg_depends_release(struct nhg_hash_entry *nhe)
+{
+ if (!zebra_nhg_depends_is_empty(nhe)) {
+ struct nhg_connected *rb_node_dep = NULL;
+
+ frr_each_safe(nhg_connected_tree, &nhe->nhg_depends,
+ rb_node_dep) {
+ zebra_nhg_dependents_del(rb_node_dep->nhe, nhe);
+ }
+ }
+}
+
+
+struct nhg_hash_entry *zebra_nhg_lookup_id(uint32_t id)
+{
+ struct nhg_hash_entry lookup = {};
+
+ lookup.id = id;
+ return hash_lookup(zrouter.nhgs_id, &lookup);
+}
+
+static int zebra_nhg_insert_id(struct nhg_hash_entry *nhe)
+{
+ if (hash_lookup(zrouter.nhgs_id, nhe)) {
+ flog_err(
+ EC_ZEBRA_NHG_TABLE_INSERT_FAILED,
+ "Failed inserting NHG id=%u into the ID hash table, entry already exists",
+ nhe->id);
+ return -1;
+ }
+
+ hash_get(zrouter.nhgs_id, nhe, hash_alloc_intern);
+
+ return 0;
+}
+
+static void zebra_nhg_set_if(struct nhg_hash_entry *nhe, struct interface *ifp)
+{
+ nhe->ifp = ifp;
+ if_nhg_dependents_add(ifp, nhe);
+}
+
+static void
+zebra_nhg_connect_depends(struct nhg_hash_entry *nhe,
+ struct nhg_connected_tree_head nhg_depends)
+{
+ struct nhg_connected *rb_node_dep = NULL;
+
+ /* This has been allocated higher above in the stack. Could probably
+ * re-allocate and free the old stuff but just using the same memory
+ * for now. Otherwise, their might be a time trade-off for repeated
+ * alloc/frees as startup.
+ */
+ nhe->nhg_depends = nhg_depends;
+
+ /* Attach backpointer to anything that it depends on */
+ zebra_nhg_dependents_init(nhe);
+ if (!zebra_nhg_depends_is_empty(nhe)) {
+ frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) {
+ zebra_nhg_dependents_add(rb_node_dep->nhe, nhe);
+ }
+ }
+
+ /* Add the ifp now if its not a group or recursive and has ifindex */
+ if (zebra_nhg_depends_is_empty(nhe) && nhe->nhg->nexthop
+ && nhe->nhg->nexthop->ifindex) {
+ struct interface *ifp = NULL;
+
+ ifp = if_lookup_by_index(nhe->nhg->nexthop->ifindex,
+ nhe->vrf_id);
+ if (ifp)
+ zebra_nhg_set_if(nhe, ifp);
+ else
+ flog_err(
+ EC_ZEBRA_IF_LOOKUP_FAILED,
+ "Zebra failed to lookup an interface with ifindex=%d in vrf=%u for NHE id=%u",
+ nhe->nhg->nexthop->ifindex, nhe->vrf_id,
+ nhe->id);
+ }
+}
+
+static struct nhg_hash_entry *zebra_nhg_copy(struct nhg_hash_entry *copy,
+ uint32_t id)
+{
+ struct nhg_hash_entry *nhe;
+
+ nhe = XCALLOC(MTYPE_NHG, sizeof(struct nhg_hash_entry));
+
+ nhe->id = id;
+
+ nhe->nhg = nexthop_group_new();
+ nexthop_group_copy(nhe->nhg, copy->nhg);
+
+ nhe->vrf_id = copy->vrf_id;
+ nhe->afi = copy->afi;
+ nhe->type = copy->type ? copy->type : ZEBRA_ROUTE_NHG;
+ nhe->refcnt = 0;
+ nhe->dplane_ref = zebra_router_get_next_sequence();
+
+ return nhe;
+}
+
+/* Allocation via hash handler */
+static void *zebra_nhg_hash_alloc(void *arg)
+{
+ struct nhg_hash_entry *nhe = NULL;
+ struct nhg_hash_entry *copy = arg;
+
+ nhe = zebra_nhg_copy(copy, copy->id);
+
+ /* Mark duplicate nexthops in a group at creation time. */
+ nexthop_group_mark_duplicates(nhe->nhg);
+
+ zebra_nhg_connect_depends(nhe, copy->nhg_depends);
+ zebra_nhg_insert_id(nhe);
+
+ return nhe;
+}
+
+uint32_t zebra_nhg_hash_key(const void *arg)
+{
+ const struct nhg_hash_entry *nhe = arg;
+
+ uint32_t key = 0x5a351234;
+
+ key = jhash_3words(nhe->vrf_id, nhe->afi, nexthop_group_hash(nhe->nhg),
+ key);
+
+ return key;
+}
+
+uint32_t zebra_nhg_id_key(const void *arg)
+{
+ const struct nhg_hash_entry *nhe = arg;
+
+ return nhe->id;
+}
+
+bool zebra_nhg_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct nhg_hash_entry *nhe1 = arg1;
+ const struct nhg_hash_entry *nhe2 = arg2;
+
+ /* No matter what if they equal IDs, assume equal */
+ if (nhe1->id && nhe2->id && (nhe1->id == nhe2->id))
+ return true;
+
+ if (nhe1->vrf_id != nhe2->vrf_id)
+ return false;
+
+ if (nhe1->afi != nhe2->afi)
+ return false;
+
+ if (nexthop_group_active_nexthop_num_no_recurse(nhe1->nhg)
+ != nexthop_group_active_nexthop_num_no_recurse(nhe2->nhg))
+ return false;
+
+ if (!nexthop_group_equal_no_recurse(nhe1->nhg, nhe2->nhg))
+ return false;
+
+ return true;
+}
+
+bool zebra_nhg_hash_id_equal(const void *arg1, const void *arg2)
+{
+ const struct nhg_hash_entry *nhe1 = arg1;
+ const struct nhg_hash_entry *nhe2 = arg2;
+
+ return nhe1->id == nhe2->id;
+}
+
+static int zebra_nhg_process_grp(struct nexthop_group *nhg,
+ struct nhg_connected_tree_head *depends,
+ struct nh_grp *grp, uint8_t count)
+{
+ nhg_connected_tree_init(depends);
+
+ for (int i = 0; i < count; i++) {
+ struct nhg_hash_entry *depend = NULL;
+ /* We do not care about nexthop_grp.weight at
+ * this time. But we should figure out
+ * how to adapt this to our code in
+ * the future.
+ */
+ depend = depends_find_id_add(depends, grp[i].id);
+
+ if (!depend) {
+ flog_err(
+ EC_ZEBRA_NHG_SYNC,
+ "Received Nexthop Group from the kernel with a dependent Nexthop ID (%u) which we do not have in our table",
+ grp[i].id);
+ return -1;
+ }
+
+ /*
+ * If this is a nexthop with its own group
+ * dependencies, add them as well. Not sure its
+ * even possible to have a group within a group
+ * in the kernel.
+ */
+
+ copy_nexthops(&nhg->nexthop, depend->nhg->nexthop, NULL);
+ }
+
+ return 0;
+}
+
+static void handle_recursive_depend(struct nhg_connected_tree_head *nhg_depends,
+ struct nexthop *nh, afi_t afi)
+{
+ struct nhg_hash_entry *depend = NULL;
+ struct nexthop_group resolved_ng = {};
+
+ _nexthop_group_add_sorted(&resolved_ng, nh);
+
+ depend = zebra_nhg_rib_find(0, &resolved_ng, afi);
+ depends_add(nhg_depends, depend);
+}
+
+static bool zebra_nhg_find(struct nhg_hash_entry **nhe, uint32_t id,
+ struct nexthop_group *nhg,
+ struct nhg_connected_tree_head *nhg_depends,
+ vrf_id_t vrf_id, afi_t afi, int type)
+{
+ struct nhg_hash_entry lookup = {};
+
+ uint32_t old_id_counter = id_counter;
+
+ bool created = false;
+ bool recursive = false;
+
+ /*
+ * If it has an id at this point, we must have gotten it from the kernel
+ */
+ lookup.id = id ? id : ++id_counter;
+
+ lookup.type = type ? type : ZEBRA_ROUTE_NHG;
+ lookup.nhg = nhg;
+
+ if (lookup.nhg->nexthop->next) {
+ /* Groups can have all vrfs and AF's in them */
+ lookup.afi = AFI_UNSPEC;
+ lookup.vrf_id = 0;
+ } else {
+ switch (lookup.nhg->nexthop->type) {
+ case (NEXTHOP_TYPE_IFINDEX):
+ case (NEXTHOP_TYPE_BLACKHOLE):
+ /*
+ * This switch case handles setting the afi different
+ * for ipv4/v6 routes. Ifindex/blackhole nexthop
+ * objects cannot be ambiguous, they must be Address
+ * Family specific. If we get here, we will either use
+ * the AF of the route, or the one we got passed from
+ * here from the kernel.
+ */
+ lookup.afi = afi;
+ break;
+ case (NEXTHOP_TYPE_IPV4_IFINDEX):
+ case (NEXTHOP_TYPE_IPV4):
+ lookup.afi = AFI_IP;
+ break;
+ case (NEXTHOP_TYPE_IPV6_IFINDEX):
+ case (NEXTHOP_TYPE_IPV6):
+ lookup.afi = AFI_IP6;
+ break;
+ }
+
+ lookup.vrf_id = vrf_id;
+ }
+
+ if (id)
+ (*nhe) = zebra_nhg_lookup_id(id);
+ else
+ (*nhe) = hash_lookup(zrouter.nhgs, &lookup);
+
+ /* If it found an nhe in our tables, this new ID is unused */
+ if (*nhe)
+ id_counter = old_id_counter;
+
+ if (!(*nhe)) {
+ /* Only hash/lookup the depends if the first lookup
+ * fails to find something. This should hopefully save a
+ * lot of cycles for larger ecmp sizes.
+ */
+ if (nhg_depends)
+ /* If you don't want to hash on each nexthop in the
+ * nexthop group struct you can pass the depends
+ * directly. Kernel-side we do this since it just looks
+ * them up via IDs.
+ */
+ lookup.nhg_depends = *nhg_depends;
+ else {
+ if (nhg->nexthop->next) {
+ zebra_nhg_depends_init(&lookup);
+
+ /* If its a group, create a dependency tree */
+ struct nexthop *nh = NULL;
+
+ for (nh = nhg->nexthop; nh; nh = nh->next)
+ depends_find_add(&lookup.nhg_depends,
+ nh, afi);
+ } else if (CHECK_FLAG(nhg->nexthop->flags,
+ NEXTHOP_FLAG_RECURSIVE)) {
+ zebra_nhg_depends_init(&lookup);
+ handle_recursive_depend(&lookup.nhg_depends,
+ nhg->nexthop->resolved,
+ afi);
+ recursive = true;
+ }
+ }
+
+ (*nhe) = hash_get(zrouter.nhgs, &lookup, zebra_nhg_hash_alloc);
+ created = true;
+
+ if (recursive)
+ SET_FLAG((*nhe)->flags, NEXTHOP_GROUP_RECURSIVE);
+ }
+ return created;
+}
+
+/* Find/create a single nexthop */
+static struct nhg_hash_entry *
+zebra_nhg_find_nexthop(uint32_t id, struct nexthop *nh, afi_t afi, int type)
+{
+ struct nhg_hash_entry *nhe = NULL;
+ struct nexthop_group nhg = {};
+
+ _nexthop_group_add_sorted(&nhg, nh);
+
+ zebra_nhg_find(&nhe, id, &nhg, NULL, nh->vrf_id, afi, 0);
+
+ return nhe;
+}
+
+static struct nhg_ctx *nhg_ctx_new()
+{
+ struct nhg_ctx *new = NULL;
+
+ new = XCALLOC(MTYPE_NHG_CTX, sizeof(struct nhg_ctx));
+
+ return new;
+}
+
+static void nhg_ctx_free(struct nhg_ctx *ctx)
+{
+ XFREE(MTYPE_NHG_CTX, ctx);
+}
+
+static uint32_t nhg_ctx_get_id(const struct nhg_ctx *ctx)
+{
+ return ctx->id;
+}
+
+static void nhg_ctx_set_status(struct nhg_ctx *ctx, enum nhg_ctx_status status)
+{
+ ctx->status = status;
+}
+
+static enum nhg_ctx_status nhg_ctx_get_status(const struct nhg_ctx *ctx)
+{
+ return ctx->status;
+}
+
+static void nhg_ctx_set_op(struct nhg_ctx *ctx, enum nhg_ctx_op_e op)
+{
+ ctx->op = op;
+}
+
+static enum nhg_ctx_op_e nhg_ctx_get_op(const struct nhg_ctx *ctx)
+{
+ return ctx->op;
+}
+
+static vrf_id_t nhg_ctx_get_vrf_id(const struct nhg_ctx *ctx)
+{
+ return ctx->vrf_id;
+}
+
+static int nhg_ctx_get_type(const struct nhg_ctx *ctx)
+{
+ return ctx->type;
+}
+
+static int nhg_ctx_get_afi(const struct nhg_ctx *ctx)
+{
+ return ctx->afi;
+}
+
+static struct nexthop *nhg_ctx_get_nh(struct nhg_ctx *ctx)
+{
+ return &ctx->u.nh;
+}
+
+static uint8_t nhg_ctx_get_count(const struct nhg_ctx *ctx)
+{
+ return ctx->count;
+}
+
+static struct nh_grp *nhg_ctx_get_grp(struct nhg_ctx *ctx)
+{
+ return ctx->u.grp;
+}
+
+static struct nhg_ctx *nhg_ctx_init(uint32_t id, struct nexthop *nh,
+ struct nh_grp *grp, vrf_id_t vrf_id,
+ afi_t afi, int type, uint8_t count)
+{
+ struct nhg_ctx *ctx = NULL;
+
+ ctx = nhg_ctx_new();
+
+ ctx->id = id;
+ ctx->vrf_id = vrf_id;
+ ctx->afi = afi;
+ ctx->type = type;
+ ctx->count = count;
+
+ if (count)
+ /* Copy over the array */
+ memcpy(&ctx->u.grp, grp, count * sizeof(struct nh_grp));
+ else if (nh)
+ ctx->u.nh = *nh;
+
+ return ctx;
+}
+
+static bool zebra_nhg_contains_unhashable(struct nhg_hash_entry *nhe)
+{
+ struct nhg_connected *rb_node_dep = NULL;
+
+ frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) {
+ if (CHECK_FLAG(rb_node_dep->nhe->flags,
+ NEXTHOP_GROUP_UNHASHABLE))
+ return true;
+ }
+
+ return false;
+}
+
+static void zebra_nhg_set_unhashable(struct nhg_hash_entry *nhe)
+{
+ SET_FLAG(nhe->flags, NEXTHOP_GROUP_UNHASHABLE);
+ SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
+
+ flog_warn(
+ EC_ZEBRA_DUPLICATE_NHG_MESSAGE,
+ "Nexthop Group with ID (%d) is a duplicate, therefore unhashable, ignoring",
+ nhe->id);
+}
+
+static void zebra_nhg_set_valid(struct nhg_hash_entry *nhe)
+{
+ struct nhg_connected *rb_node_dep;
+
+ SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID);
+
+ frr_each(nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep)
+ zebra_nhg_set_valid(rb_node_dep->nhe);
+}
+
+static void zebra_nhg_set_invalid(struct nhg_hash_entry *nhe)
+{
+ struct nhg_connected *rb_node_dep;
+
+ UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID);
+
+ /* Update validity of nexthops depending on it */
+ frr_each(nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep)
+ zebra_nhg_check_valid(rb_node_dep->nhe);
+}
+
+void zebra_nhg_check_valid(struct nhg_hash_entry *nhe)
+{
+ struct nhg_connected *rb_node_dep = NULL;
+ bool valid = false;
+
+ /* If anthing else in the group is valid, the group is valid */
+ frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) {
+ if (CHECK_FLAG(rb_node_dep->nhe->flags, NEXTHOP_GROUP_VALID)) {
+ valid = true;
+ goto done;
+ }
+ }
+
+done:
+ if (valid)
+ zebra_nhg_set_valid(nhe);
+ else
+ zebra_nhg_set_invalid(nhe);
+}
+
+
+static void zebra_nhg_release(struct nhg_hash_entry *nhe)
+{
+ /* Remove it from any lists it may be on */
+ zebra_nhg_depends_release(nhe);
+ zebra_nhg_dependents_release(nhe);
+ if (nhe->ifp)
+ if_nhg_dependents_del(nhe->ifp, nhe);
+
+ /*
+ * If its unhashable, we didn't store it here and have to be
+ * sure we don't clear one thats actually being used.
+ */
+ if (!CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_UNHASHABLE))
+ hash_release(zrouter.nhgs, nhe);
+
+ hash_release(zrouter.nhgs_id, nhe);
+}
+
+static void zebra_nhg_handle_uninstall(struct nhg_hash_entry *nhe)
+{
+ zebra_nhg_release(nhe);
+ zebra_nhg_free(nhe);
+}
+
+static void zebra_nhg_handle_install(struct nhg_hash_entry *nhe)
+{
+ /* Update validity of groups depending on it */
+ struct nhg_connected *rb_node_dep;
+
+ frr_each_safe(nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep)
+ zebra_nhg_set_valid(rb_node_dep->nhe);
+}
+
+/*
+ * The kernel/other program has changed the state of a nexthop object we are
+ * using.
+ */
+static void zebra_nhg_handle_kernel_state_change(struct nhg_hash_entry *nhe,
+ bool is_delete)
+{
+ if (nhe->refcnt) {
+ flog_err(
+ EC_ZEBRA_NHG_SYNC,
+ "Kernel %s a nexthop group with ID (%u) that we are still using for a route, sending it back down",
+ (is_delete ? "deleted" : "updated"), nhe->id);
+
+ UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
+ zebra_nhg_install_kernel(nhe);
+ } else
+ zebra_nhg_handle_uninstall(nhe);
+}
+
+static int nhg_ctx_process_new(struct nhg_ctx *ctx)
+{
+ struct nexthop_group *nhg = NULL;
+ struct nhg_connected_tree_head nhg_depends = {};
+ struct nhg_hash_entry *lookup = NULL;
+ struct nhg_hash_entry *nhe = NULL;
+
+ uint32_t id = nhg_ctx_get_id(ctx);
+ uint8_t count = nhg_ctx_get_count(ctx);
+ vrf_id_t vrf_id = nhg_ctx_get_vrf_id(ctx);
+ int type = nhg_ctx_get_type(ctx);
+ afi_t afi = nhg_ctx_get_afi(ctx);
+
+ lookup = zebra_nhg_lookup_id(id);
+
+ if (lookup) {
+ /* This is already present in our table, hence an update
+ * that we did not initate.
+ */
+ zebra_nhg_handle_kernel_state_change(lookup, false);
+ return 0;
+ }
+
+ if (nhg_ctx_get_count(ctx)) {
+ nhg = nexthop_group_new();
+ if (zebra_nhg_process_grp(nhg, &nhg_depends,
+ nhg_ctx_get_grp(ctx), count)) {
+ depends_decrement_free(&nhg_depends);
+ nexthop_group_delete(&nhg);
+ return -ENOENT;
+ }
+
+ if (!zebra_nhg_find(&nhe, id, nhg, &nhg_depends, vrf_id, type,
+ afi))
+ depends_decrement_free(&nhg_depends);
+
+ /* These got copied over in zebra_nhg_alloc() */
+ nexthop_group_delete(&nhg);
+ } else
+ nhe = zebra_nhg_find_nexthop(id, nhg_ctx_get_nh(ctx), afi,
+ type);
+
+ if (nhe) {
+ if (id != nhe->id) {
+ struct nhg_hash_entry *kernel_nhe = NULL;
+
+ /* Duplicate but with different ID from
+ * the kernel
+ */
+
+ /* The kernel allows duplicate nexthops
+ * as long as they have different IDs.
+ * We are ignoring those to prevent
+ * syncing problems with the kernel
+ * changes.
+ *
+ * We maintain them *ONLY* in the ID hash table to
+ * track them and set the flag to indicated
+ * their attributes are unhashable.
+ */
+
+ kernel_nhe = zebra_nhg_copy(nhe, id);
+ zebra_nhg_insert_id(kernel_nhe);
+ zebra_nhg_set_unhashable(kernel_nhe);
+ } else if (zebra_nhg_contains_unhashable(nhe)) {
+ /* The group we got contains an unhashable/duplicated
+ * depend, so lets mark this group as unhashable as well
+ * and release it from the non-ID hash.
+ */
+ hash_release(zrouter.nhgs, nhe);
+ zebra_nhg_set_unhashable(nhe);
+ } else {
+ /* It actually created a new nhe */
+ SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID);
+ SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
+ }
+ } else {
+ flog_err(
+ EC_ZEBRA_TABLE_LOOKUP_FAILED,
+ "Zebra failed to find or create a nexthop hash entry for ID (%u)",
+ id);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int nhg_ctx_process_del(struct nhg_ctx *ctx)
+{
+ struct nhg_hash_entry *nhe = NULL;
+ uint32_t id = nhg_ctx_get_id(ctx);
+
+ nhe = zebra_nhg_lookup_id(id);
+
+ if (!nhe) {
+ flog_warn(
+ EC_ZEBRA_BAD_NHG_MESSAGE,
+ "Kernel delete message received for nexthop group ID (%u) that we do not have in our ID table",
+ id);
+ return -1;
+ }
+
+ zebra_nhg_handle_kernel_state_change(nhe, true);
+
+ return 0;
+}
+
+static void nhg_ctx_process_finish(struct nhg_ctx *ctx)
+{
+ struct nexthop *nh;
+
+ /*
+ * Just freeing for now, maybe do something more in the future
+ * based on flag.
+ */
+
+ if (nhg_ctx_get_count(ctx))
+ goto done;
+
+ nh = nhg_ctx_get_nh(ctx);
+
+ nexthop_del_labels(nh);
+
+done:
+ nhg_ctx_free(ctx);
+}
+
+static int queue_add(struct nhg_ctx *ctx)
+{
+ /* If its queued or already processed do nothing */
+ if (nhg_ctx_get_status(ctx) == NHG_CTX_QUEUED)
+ return 0;
+
+ if (rib_queue_nhg_add(ctx)) {
+ nhg_ctx_set_status(ctx, NHG_CTX_FAILURE);
+ return -1;
+ }
+
+ nhg_ctx_set_status(ctx, NHG_CTX_QUEUED);
+
+ return 0;
+}
+
+int nhg_ctx_process(struct nhg_ctx *ctx)
+{
+ int ret = 0;
+
+ switch (nhg_ctx_get_op(ctx)) {
+ case NHG_CTX_OP_NEW:
+ ret = nhg_ctx_process_new(ctx);
+ if (nhg_ctx_get_count(ctx) && ret == -ENOENT
+ && nhg_ctx_get_status(ctx) != NHG_CTX_REQUEUED) {
+ /**
+ * We have entered a situation where we are
+ * processing a group from the kernel
+ * that has a contained nexthop which
+ * we have not yet processed.
+ *
+ * Re-enqueue this ctx to be handled exactly one
+ * more time (indicated by the flag).
+ *
+ * By the time we get back to it, we
+ * should have processed its depends.
+ */
+ nhg_ctx_set_status(ctx, NHG_CTX_NONE);
+ if (queue_add(ctx) == 0) {
+ nhg_ctx_set_status(ctx, NHG_CTX_REQUEUED);
+ return 0;
+ }
+ }
+ break;
+ case NHG_CTX_OP_DEL:
+ ret = nhg_ctx_process_del(ctx);
+ case NHG_CTX_OP_NONE:
+ break;
+ }
+
+ nhg_ctx_set_status(ctx, (ret ? NHG_CTX_FAILURE : NHG_CTX_SUCCESS));
+
+ nhg_ctx_process_finish(ctx);
+
+ return ret;
+}
+
+/* Kernel-side, you either get a single new nexthop or a array of ID's */
+int zebra_nhg_kernel_find(uint32_t id, struct nexthop *nh, struct nh_grp *grp,
+ uint8_t count, vrf_id_t vrf_id, afi_t afi, int type,
+ int startup)
+{
+ struct nhg_ctx *ctx = NULL;
+
+ if (id > id_counter)
+ /* Increase our counter so we don't try to create
+ * an ID that already exists
+ */
+ id_counter = id;
+
+ ctx = nhg_ctx_init(id, nh, grp, vrf_id, afi, type, count);
+ nhg_ctx_set_op(ctx, NHG_CTX_OP_NEW);
+
+ /* Under statup conditions, we need to handle them immediately
+ * like we do for routes. Otherwise, we are going to get a route
+ * with a nhe_id that we have not handled.
+ */
+ if (startup)
+ return nhg_ctx_process(ctx);
+
+ if (queue_add(ctx)) {
+ nhg_ctx_process_finish(ctx);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Kernel-side, received delete message */
+int zebra_nhg_kernel_del(uint32_t id)
+{
+ struct nhg_ctx *ctx = NULL;
+
+ ctx = nhg_ctx_init(id, NULL, NULL, 0, 0, 0, 0);
+
+ nhg_ctx_set_op(ctx, NHG_CTX_OP_DEL);
+
+ if (queue_add(ctx)) {
+ nhg_ctx_process_finish(ctx);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Some dependency helper functions */
+static struct nhg_hash_entry *depends_find(struct nexthop *nh, afi_t afi)
+{
+ struct nexthop *lookup = NULL;
+ struct nhg_hash_entry *nhe = NULL;
+
+ copy_nexthops(&lookup, nh, NULL);
+
+ /* Clear it, in case its a group */
+ nexthops_free(lookup->next);
+ nexthops_free(lookup->prev);
+ lookup->next = NULL;
+ lookup->prev = NULL;
+
+ nhe = zebra_nhg_find_nexthop(0, lookup, afi, 0);
+
+ nexthops_free(lookup);
+
+ return nhe;
+}
+
+static void depends_add(struct nhg_connected_tree_head *head,
+ struct nhg_hash_entry *depend)
+{
+ nhg_connected_tree_add_nhe(head, depend);
+ zebra_nhg_increment_ref(depend);
+}
+
+static struct nhg_hash_entry *
+depends_find_add(struct nhg_connected_tree_head *head, struct nexthop *nh,
+ afi_t afi)
+{
+ struct nhg_hash_entry *depend = NULL;
+
+ depend = depends_find(nh, afi);
+
+ if (depend)
+ depends_add(head, depend);
+
+ return depend;
+}
+
+static struct nhg_hash_entry *
+depends_find_id_add(struct nhg_connected_tree_head *head, uint32_t id)
+{
+ struct nhg_hash_entry *depend = NULL;
+
+ depend = zebra_nhg_lookup_id(id);
+
+ if (depend)
+ depends_add(head, depend);
+
+ return depend;
+}
+
+static void depends_decrement_free(struct nhg_connected_tree_head *head)
+{
+ nhg_connected_tree_decrement_ref(head);
+ nhg_connected_tree_free(head);
+}
+
+/* Rib-side, you get a nexthop group struct */
+struct nhg_hash_entry *
+zebra_nhg_rib_find(uint32_t id, struct nexthop_group *nhg, afi_t rt_afi)
+{
+ struct nhg_hash_entry *nhe = NULL;
+
+ if (!(nhg && nhg->nexthop)) {
+ flog_err(EC_ZEBRA_TABLE_LOOKUP_FAILED,
+ "No nexthop passed to %s", __func__);
+ return NULL;
+ }
+
+ zebra_nhg_find(&nhe, id, nhg, NULL, nhg->nexthop->vrf_id, rt_afi, 0);
+
+ return nhe;
+}
+
+static void zebra_nhg_free_members(struct nhg_hash_entry *nhe)
+{
+ nexthop_group_delete(&nhe->nhg);
+ /* Decrement to remove connection ref */
+ nhg_connected_tree_decrement_ref(&nhe->nhg_depends);
+ nhg_connected_tree_free(&nhe->nhg_depends);
+ nhg_connected_tree_free(&nhe->nhg_dependents);
+}
+
+void zebra_nhg_free(void *arg)
+{
+ struct nhg_hash_entry *nhe = NULL;
+
+ nhe = (struct nhg_hash_entry *)arg;
+
+ if (nhe->refcnt)
+ zlog_debug("nhe_id=%u hash refcnt=%d", nhe->id, nhe->refcnt);
+
+ zebra_nhg_free_members(nhe);
+
+ XFREE(MTYPE_NHG, nhe);
+}
+
+void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe)
+{
+ nhe->refcnt--;
+
+ if (!zebra_nhg_depends_is_empty(nhe))
+ nhg_connected_tree_decrement_ref(&nhe->nhg_depends);
+
+ if (ZEBRA_NHG_CREATED(nhe) && nhe->refcnt <= 0)
+ zebra_nhg_uninstall_kernel(nhe);
+}
+
+void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe)
+{
+ nhe->refcnt++;
+
+ if (!zebra_nhg_depends_is_empty(nhe))
+ nhg_connected_tree_increment_ref(&nhe->nhg_depends);
+}
static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop,
struct nexthop *nexthop)
@@ -152,7 +1231,8 @@ static bool nexthop_valid_resolve(const struct nexthop *nexthop,
/*
* Given a nexthop we need to properly recursively resolve
* the route. As such, do a table lookup to find and match
- * if at all possible. Set the nexthop->ifindex as appropriate
+ * if at all possible. Set the nexthop->ifindex and resolved_id
+ * as appropriate
*/
static int nexthop_active(afi_t afi, struct route_entry *re,
struct nexthop *nexthop, struct route_node *top)
@@ -171,6 +1251,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
|| nexthop->type == NEXTHOP_TYPE_IPV6)
nexthop->ifindex = 0;
+
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
nexthops_free(nexthop->resolved);
nexthop->resolved = NULL;
@@ -210,13 +1291,12 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
if (connected_is_unnumbered(ifp)) {
if (if_is_operative(ifp))
return 1;
- else {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug(
- "\t%s: Onlink and interface %s is not operative",
- __PRETTY_FUNCTION__, ifp->name);
- return 0;
- }
+
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug(
+ "\t%s: Onlink and interface %s is not operative",
+ __PRETTY_FUNCTION__, ifp->name);
+ return 0;
}
if (!if_is_operative(ifp)) {
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
@@ -276,7 +1356,8 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
/* Pick up selected route. */
/* However, do not resolve over default route unless explicitly
- * allowed. */
+ * allowed.
+ */
if (is_default_prefix(&rn->p)
&& !rnh_resolve_via_default(zvrf, p.family)) {
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
@@ -294,7 +1375,8 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
match = dest->selected_fib;
/* If there is no selected route or matched route is EGP, go up
- tree. */
+ * tree.
+ */
if (!match) {
do {
rn = rn->parent;
@@ -307,7 +1389,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
if (match->type == ZEBRA_ROUTE_CONNECT) {
/* Directly point connected route. */
- newhop = match->ng.nexthop;
+ newhop = match->ng->nexthop;
if (newhop) {
if (nexthop->type == NEXTHOP_TYPE_IPV4
|| nexthop->type == NEXTHOP_TYPE_IPV6)
@@ -316,7 +1398,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
return 1;
} else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) {
resolved = 0;
- for (ALL_NEXTHOPS(match->ng, newhop)) {
+ for (ALL_NEXTHOPS_PTR(match->ng, newhop)) {
if (!CHECK_FLAG(match->status,
ROUTE_ENTRY_INSTALLED))
continue;
@@ -330,13 +1412,14 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
}
if (resolved)
re->nexthop_mtu = match->mtu;
+
if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED)
zlog_debug("\t%s: Recursion failed to find",
__PRETTY_FUNCTION__);
return resolved;
} else if (re->type == ZEBRA_ROUTE_STATIC) {
resolved = 0;
- for (ALL_NEXTHOPS(match->ng, newhop)) {
+ for (ALL_NEXTHOPS_PTR(match->ng, newhop)) {
if (!CHECK_FLAG(match->status,
ROUTE_ENTRY_INSTALLED))
continue;
@@ -382,6 +1465,9 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
* appropriately as well. An existing route map can turn
* (otherwise active) nexthop into inactive, but not vice versa.
*
+ * If it finds a nexthop recursivedly, set the resolved_id
+ * to match that nexthop's nhg_hash_entry ID;
+ *
* The return value is the final value of 'ACTIVE' flag.
*/
static unsigned nexthop_active_check(struct route_node *rn,
@@ -505,23 +1591,29 @@ static unsigned nexthop_active_check(struct route_node *rn,
/*
* Iterate over all nexthops of the given RIB entry and refresh their
- * ACTIVE flag. re->nexthop_active_num is updated accordingly. If any
- * nexthop is found to toggle the ACTIVE flag, the whole re structure
- * is flagged with ROUTE_ENTRY_CHANGED.
+ * ACTIVE flag. If any nexthop is found to toggle the ACTIVE flag,
+ * the whole re structure is flagged with ROUTE_ENTRY_CHANGED.
*
* Return value is the new number of active nexthops.
*/
int nexthop_active_update(struct route_node *rn, struct route_entry *re)
{
+ struct nexthop_group new_grp = {};
struct nexthop *nexthop;
union g_addr prev_src;
unsigned int prev_active, new_active;
ifindex_t prev_index;
+ uint8_t curr_active = 0;
+
+ afi_t rt_afi = family2afi(rn->p.family);
- re->nexthop_active_num = 0;
UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
- for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) {
+ /* Copy over the nexthops in current state */
+ nexthop_group_copy(&new_grp, re->ng);
+
+ for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next) {
+
/* No protocol daemon provides src and so we're skipping
* tracking it */
prev_src = nexthop->rmap_src;
@@ -533,14 +1625,19 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re)
* a multipath perpsective should not be a data plane
* decision point.
*/
- new_active = nexthop_active_check(rn, re, nexthop);
+ new_active =
+ nexthop_active_check(rn, re, nexthop);
+
if (new_active
- && re->nexthop_active_num >= zrouter.multipath_num) {
+ && nexthop_group_active_nexthop_num(&new_grp)
+ >= zrouter.multipath_num) {
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
new_active = 0;
}
+
if (new_active)
- re->nexthop_active_num++;
+ curr_active++;
+
/* Don't allow src setting on IPv6 addr for now */
if (prev_active != new_active || prev_index != nexthop->ifindex
|| ((nexthop->type >= NEXTHOP_TYPE_IFINDEX
@@ -555,6 +1652,269 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re)
SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
}
- return re->nexthop_active_num;
+ if (CHECK_FLAG(re->status, ROUTE_ENTRY_CHANGED)) {
+ struct nhg_hash_entry *new_nhe = NULL;
+
+ new_nhe = zebra_nhg_rib_find(0, &new_grp, rt_afi);
+
+ zebra_nhg_re_update_ref(re, new_nhe);
+ }
+
+ if (curr_active) {
+ struct nhg_hash_entry *nhe = NULL;
+
+ nhe = zebra_nhg_lookup_id(re->nhe_id);
+
+ if (nhe)
+ SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID);
+ else
+ flog_err(
+ EC_ZEBRA_TABLE_LOOKUP_FAILED,
+ "Active update on NHE id=%u that we do not have in our tables",
+ re->nhe_id);
+ }
+
+ /*
+ * Do not need these nexthops anymore since they
+ * were either copied over into an nhe or not
+ * used at all.
+ */
+ nexthops_free(new_grp.nexthop);
+ return curr_active;
}
+static void zebra_nhg_re_attach_ref(struct route_entry *re,
+ struct nhg_hash_entry *new)
+{
+ re->ng = new->nhg;
+ re->nhe_id = new->id;
+
+ zebra_nhg_increment_ref(new);
+}
+
+int zebra_nhg_re_update_ref(struct route_entry *re, struct nhg_hash_entry *new)
+{
+ struct nhg_hash_entry *old = NULL;
+ int ret = 0;
+
+ if (new == NULL) {
+ re->ng = NULL;
+ goto done;
+ }
+
+ if (re->nhe_id != new->id) {
+ old = zebra_nhg_lookup_id(re->nhe_id);
+
+ zebra_nhg_re_attach_ref(re, new);
+
+ if (old)
+ zebra_nhg_decrement_ref(old);
+ } else if (!re->ng)
+ /* This is the first time it's being attached */
+ zebra_nhg_re_attach_ref(re, new);
+
+done:
+ return ret;
+}
+
+/* Convert a nhe into a group array */
+uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe,
+ int max_num)
+{
+ struct nhg_connected *rb_node_dep = NULL;
+ struct nhg_hash_entry *depend = NULL;
+ uint8_t i = 0;
+
+ frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) {
+ bool duplicate = false;
+
+ depend = rb_node_dep->nhe;
+
+ /*
+ * If its recursive, use its resolved nhe in the group
+ */
+ if (CHECK_FLAG(depend->flags, NEXTHOP_GROUP_RECURSIVE)) {
+ depend = zebra_nhg_resolve(depend);
+ if (!depend) {
+ flog_err(
+ EC_ZEBRA_NHG_FIB_UPDATE,
+ "Failed to recursively resolve Nexthop Hash Entry in the group id=%u",
+ nhe->id);
+ continue;
+ }
+ }
+
+ /* Check for duplicate IDs, kernel doesn't like that */
+ for (int j = 0; j < i; j++) {
+ if (depend->id == grp[j].id)
+ duplicate = true;
+ }
+
+ if (!duplicate) {
+ grp[i].id = depend->id;
+ /* We aren't using weights for anything right now */
+ grp[i].weight = 0;
+ i++;
+ }
+
+ if (i >= max_num)
+ goto done;
+ }
+
+done:
+ return i;
+}
+
+void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe)
+{
+ struct nhg_connected *rb_node_dep = NULL;
+
+ /* Resolve it first */
+ nhe = zebra_nhg_resolve(nhe);
+
+ /* Make sure all depends are installed/queued */
+ frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) {
+ zebra_nhg_install_kernel(rb_node_dep->nhe);
+ }
+
+ if (!CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED)
+ && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED)) {
+ /* Change its type to us since we are installing it */
+ nhe->type = ZEBRA_ROUTE_NHG;
+
+ int ret = dplane_nexthop_add(nhe);
+
+ switch (ret) {
+ case ZEBRA_DPLANE_REQUEST_QUEUED:
+ SET_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED);
+ break;
+ case ZEBRA_DPLANE_REQUEST_FAILURE:
+ flog_err(
+ EC_ZEBRA_DP_INSTALL_FAIL,
+ "Failed to install Nexthop ID (%u) into the kernel",
+ nhe->id);
+ break;
+ case ZEBRA_DPLANE_REQUEST_SUCCESS:
+ SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
+ zebra_nhg_handle_install(nhe);
+ break;
+ }
+ }
+}
+
+void zebra_nhg_uninstall_kernel(struct nhg_hash_entry *nhe)
+{
+ if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED)) {
+ int ret = dplane_nexthop_delete(nhe);
+
+ switch (ret) {
+ case ZEBRA_DPLANE_REQUEST_QUEUED:
+ SET_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED);
+ break;
+ case ZEBRA_DPLANE_REQUEST_FAILURE:
+ flog_err(
+ EC_ZEBRA_DP_DELETE_FAIL,
+ "Failed to uninstall Nexthop ID (%u) from the kernel",
+ nhe->id);
+ break;
+ case ZEBRA_DPLANE_REQUEST_SUCCESS:
+ UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
+ break;
+ }
+ }
+
+ zebra_nhg_handle_uninstall(nhe);
+}
+
+void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx)
+{
+ enum dplane_op_e op;
+ enum zebra_dplane_result status;
+ uint32_t id = 0;
+ struct nhg_hash_entry *nhe = NULL;
+
+ op = dplane_ctx_get_op(ctx);
+ status = dplane_ctx_get_status(ctx);
+
+ id = dplane_ctx_get_nhe_id(ctx);
+
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
+ zlog_debug(
+ "Nexthop dplane ctx %p, op %s, nexthop ID (%u), result %s",
+ ctx, dplane_op2str(op), id, dplane_res2str(status));
+
+ switch (op) {
+ case DPLANE_OP_NH_DELETE:
+ if (status != ZEBRA_DPLANE_REQUEST_SUCCESS)
+ flog_err(
+ EC_ZEBRA_DP_DELETE_FAIL,
+ "Failed to uninstall Nexthop ID (%u) from the kernel",
+ id);
+ /* We already free'd the data, nothing to do */
+ break;
+ case DPLANE_OP_NH_INSTALL:
+ case DPLANE_OP_NH_UPDATE:
+ nhe = zebra_nhg_lookup_id(id);
+
+ if (!nhe) {
+ flog_err(
+ EC_ZEBRA_NHG_SYNC,
+ "%s operation preformed on Nexthop ID (%u) in the kernel, that we no longer have in our table",
+ dplane_op2str(op), id);
+ break;
+ }
+
+ UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED);
+ if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) {
+ SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID);
+ SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
+ zebra_nhg_handle_install(nhe);
+ } else
+ flog_err(
+ EC_ZEBRA_DP_INSTALL_FAIL,
+ "Failed to install Nexthop ID (%u) into the kernel",
+ nhe->id);
+ break;
+ case DPLANE_OP_ROUTE_INSTALL:
+ case DPLANE_OP_ROUTE_UPDATE:
+ case DPLANE_OP_ROUTE_DELETE:
+ case DPLANE_OP_ROUTE_NOTIFY:
+ case DPLANE_OP_LSP_INSTALL:
+ case DPLANE_OP_LSP_UPDATE:
+ case DPLANE_OP_LSP_DELETE:
+ case DPLANE_OP_LSP_NOTIFY:
+ case DPLANE_OP_PW_INSTALL:
+ case DPLANE_OP_PW_UNINSTALL:
+ case DPLANE_OP_SYS_ROUTE_ADD:
+ case DPLANE_OP_SYS_ROUTE_DELETE:
+ case DPLANE_OP_ADDR_INSTALL:
+ case DPLANE_OP_ADDR_UNINSTALL:
+ case DPLANE_OP_MAC_INSTALL:
+ case DPLANE_OP_MAC_DELETE:
+ case DPLANE_OP_NEIGH_INSTALL:
+ case DPLANE_OP_NEIGH_UPDATE:
+ case DPLANE_OP_NEIGH_DELETE:
+ case DPLANE_OP_VTEP_ADD:
+ case DPLANE_OP_VTEP_DELETE:
+ case DPLANE_OP_NONE:
+ break;
+ }
+
+ dplane_ctx_fini(&ctx);
+}
+
+static void zebra_nhg_sweep_entry(struct hash_bucket *bucket, void *arg)
+{
+ struct nhg_hash_entry *nhe = NULL;
+
+ nhe = (struct nhg_hash_entry *)bucket->data;
+
+ /* If its being ref'd, just let it be uninstalled via a route removal */
+ if (ZEBRA_NHG_CREATED(nhe) && nhe->refcnt <= 0)
+ zebra_nhg_uninstall_kernel(nhe);
+}
+
+void zebra_nhg_sweep_table(struct hash *hash)
+{
+ hash_iterate(hash, zebra_nhg_sweep_entry, NULL);
+}
diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h
index ff2351c759..1f695433c9 100644
--- a/zebra/zebra_nhg.h
+++ b/zebra/zebra_nhg.h
@@ -24,6 +24,205 @@
#define __ZEBRA_NHG_H__
#include "zebra/rib.h"
+#include "lib/nexthop_group.h"
+#include "zebra/zebra_dplane.h"
+
+/* This struct is used exclusively for dataplane
+ * interaction via a dataplane context.
+ *
+ * It is designed to mimic the netlink nexthop_grp
+ * struct in include/linux/nexthop.h
+ */
+struct nh_grp {
+ uint32_t id;
+ uint8_t weight;
+};
+
+PREDECL_RBTREE_UNIQ(nhg_connected_tree);
+
+/*
+ * Hashtables contiaining entries found in `zebra_router`.
+ */
+
+struct nhg_hash_entry {
+ uint32_t id;
+ afi_t afi;
+ vrf_id_t vrf_id;
+ int type;
+
+ struct nexthop_group *nhg;
+
+ /* If this is not a group, it
+ * will be a single nexthop
+ * and must have an interface
+ * associated with it.
+ * Otherwise, this will be null.
+ */
+ struct interface *ifp;
+
+ uint32_t refcnt;
+ uint32_t dplane_ref;
+
+ uint32_t flags;
+
+ /* Dependency tree for other entries.
+ * For instance a group with two
+ * nexthops will have two dependencies
+ * pointing to those nhg_hash_entries.
+ *
+ * Using a rb tree here to make lookups
+ * faster with ID's.
+ */
+ struct nhg_connected_tree_head nhg_depends, nhg_dependents;
+/*
+ * Is this nexthop group valid, ie all nexthops are fully resolved.
+ * What is fully resolved? It's a nexthop that is either self contained
+ * and correct( ie no recursive pointer ) or a nexthop that is recursively
+ * resolved and correct.
+ */
+#define NEXTHOP_GROUP_VALID (1 << 0)
+/*
+ * Has this nexthop group been installed? At this point in time, this
+ * means that the data-plane has been told about this nexthop group
+ * and it's possible usage by a route entry.
+ */
+#define NEXTHOP_GROUP_INSTALLED (1 << 1)
+/*
+ * Has the nexthop group been queued to be send to the FIB?
+ * The NEXTHOP_GROUP_VALID flag should also be set by this point.
+ */
+#define NEXTHOP_GROUP_QUEUED (1 << 2)
+/*
+ * Is this a nexthop that is recursively resolved?
+ */
+#define NEXTHOP_GROUP_RECURSIVE (1 << 3)
+/*
+ * This is a nexthop group we got from the kernel, it is identical to
+ * one we already have. (The kernel allows duplicate nexthops, we don't
+ * since we hash on them). We are only tracking it in our ID table,
+ * it is unusable by our created routes but may be used by routes we get
+ * from the kernel. Therefore, it is unhashable.
+ */
+#define NEXTHOP_GROUP_UNHASHABLE (1 << 4)
+};
+
+/* Was this one we created, either this session or previously? */
+#define ZEBRA_NHG_CREATED(NHE) ((NHE->type) == ZEBRA_ROUTE_NHG)
+
+
+enum nhg_ctx_op_e {
+ NHG_CTX_OP_NONE = 0,
+ NHG_CTX_OP_NEW,
+ NHG_CTX_OP_DEL,
+};
+
+enum nhg_ctx_status {
+ NHG_CTX_NONE = 0,
+ NHG_CTX_QUEUED,
+ NHG_CTX_REQUEUED,
+ NHG_CTX_SUCCESS,
+ NHG_CTX_FAILURE,
+};
+
+/*
+ * Context needed to queue nhg updates on the
+ * work queue.
+ */
+struct nhg_ctx {
+
+ /* Unique ID */
+ uint32_t id;
+
+ vrf_id_t vrf_id;
+ afi_t afi;
+ /*
+ * This should only every be ZEBRA_ROUTE_NHG unless we get a a kernel
+ * created nexthop not made by us.
+ */
+ int type;
+
+ /* If its a group array, how many? */
+ uint8_t count;
+
+ /* Its either a single nexthop or an array of ID's */
+ union {
+ struct nexthop nh;
+ struct nh_grp grp[MULTIPATH_NUM];
+ } u;
+
+ enum nhg_ctx_op_e op;
+ enum nhg_ctx_status status;
+};
+
+
+/**
+ * NHE abstracted tree functions.
+ * Use these where possible instead of the direct ones access ones.
+ */
+extern struct nhg_hash_entry *zebra_nhg_resolve(struct nhg_hash_entry *nhe);
+
+extern unsigned int zebra_nhg_depends_count(const struct nhg_hash_entry *nhe);
+extern bool zebra_nhg_depends_is_empty(const struct nhg_hash_entry *nhe);
+
+extern unsigned int
+zebra_nhg_dependents_count(const struct nhg_hash_entry *nhe);
+extern bool zebra_nhg_dependents_is_empty(const struct nhg_hash_entry *nhe);
+
+/* Lookup ID, doesn't create */
+extern struct nhg_hash_entry *zebra_nhg_lookup_id(uint32_t id);
+
+/* Hash functions */
+extern uint32_t zebra_nhg_hash_key(const void *arg);
+extern uint32_t zebra_nhg_id_key(const void *arg);
+
+extern bool zebra_nhg_hash_equal(const void *arg1, const void *arg2);
+extern bool zebra_nhg_hash_id_equal(const void *arg1, const void *arg2);
+
+/*
+ * Process a context off of a queue.
+ * Specifically this should be from
+ * the rib meta queue.
+ */
+extern int nhg_ctx_process(struct nhg_ctx *ctx);
+
+/* Find via kernel nh creation */
+extern int zebra_nhg_kernel_find(uint32_t id, struct nexthop *nh,
+ struct nh_grp *grp, uint8_t count,
+ vrf_id_t vrf_id, afi_t afi, int type,
+ int startup);
+/* Del via kernel */
+extern int zebra_nhg_kernel_del(uint32_t id);
+
+/* Find via route creation */
+extern struct nhg_hash_entry *
+zebra_nhg_rib_find(uint32_t id, struct nexthop_group *nhg, afi_t rt_afi);
+
+/* Reference counter functions */
+extern void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe);
+extern void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe);
+extern int zebra_nhg_re_update_ref(struct route_entry *re,
+ struct nhg_hash_entry *nhe);
+
+/* Check validity of nhe, if invalid will update dependents as well */
+extern void zebra_nhg_check_valid(struct nhg_hash_entry *nhe);
+
+/* Convert nhe depends to a grp context that can be passed around safely */
+extern uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe,
+ int size);
+
+/* Dataplane install/uninstall */
+extern void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe);
+extern void zebra_nhg_uninstall_kernel(struct nhg_hash_entry *nhe);
+
+/* Forward ref of dplane update context type */
+struct zebra_dplane_ctx;
+extern void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx);
+
+
+/* Sweet the nhg hash tables for old entries on restart */
+extern void zebra_nhg_sweep_table(struct hash *hash);
+
+/* Nexthop resolution processing */
extern int nexthop_active_update(struct route_node *rn, struct route_entry *re);
#endif
diff --git a/zebra/zebra_nhg_private.h b/zebra/zebra_nhg_private.h
new file mode 100644
index 0000000000..170e2357e3
--- /dev/null
+++ b/zebra/zebra_nhg_private.h
@@ -0,0 +1,62 @@
+/*
+ * Nexthop Group Private Functions.
+ * Copyright (C) 2019 Cumulus Networks, Inc.
+ * Stephen Worley
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * These functions should only be used internally for nhg_hash_entry
+ * manipulation and in certain special cases.
+ *
+ * Please use `zebra/zebra_nhg.h` for any general nhg_hash_entry api needs.
+ */
+
+#ifndef __ZEBRA_NHG_PRIVATE_H__
+#define __ZEBRA_NHG_PRIVATE_H__
+
+#include "zebra/zebra_nhg.h"
+
+/* Abstraction for connected trees */
+struct nhg_connected {
+ struct nhg_connected_tree_item tree_item;
+ struct nhg_hash_entry *nhe;
+};
+
+static int nhg_connected_cmp(const struct nhg_connected *con1,
+ const struct nhg_connected *con2)
+{
+ return (con1->nhe->id - con2->nhe->id);
+}
+
+DECLARE_RBTREE_UNIQ(nhg_connected_tree, struct nhg_connected, tree_item,
+ nhg_connected_cmp);
+
+/* nhg connected tree direct access functions */
+extern void nhg_connected_tree_init(struct nhg_connected_tree_head *head);
+extern void nhg_connected_tree_free(struct nhg_connected_tree_head *head);
+extern bool
+nhg_connected_tree_is_empty(const struct nhg_connected_tree_head *head);
+extern struct nhg_connected *
+nhg_connected_tree_root(struct nhg_connected_tree_head *head);
+extern void nhg_connected_tree_del_nhe(struct nhg_connected_tree_head *head,
+ struct nhg_hash_entry *nhe);
+extern void nhg_connected_tree_add_nhe(struct nhg_connected_tree_head *head,
+ struct nhg_hash_entry *nhe);
+
+extern void zebra_nhg_free(void *arg);
+
+#endif /* __ZEBRA_NHG_PRIVATE_H__ */
diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c
index 09edbc9a68..3f1567a95b 100644
--- a/zebra/zebra_pw.c
+++ b/zebra/zebra_pw.c
@@ -259,7 +259,7 @@ static int zebra_pw_check_reachability(struct zebra_pw *pw)
* Need to ensure that there's a label binding for all nexthops.
* Otherwise, ECMP for this route could render the pseudowire unusable.
*/
- for (ALL_NEXTHOPS(re->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
if (!nexthop->nh_label) {
if (IS_ZEBRA_DEBUG_PW)
zlog_debug("%s: unlabeled route for %s",
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index c2fa33f57d..e0bf1a58f2 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -56,7 +56,6 @@
#include "zebra/zebra_vxlan.h"
#include "zebra/zapi_msg.h"
#include "zebra/zebra_dplane.h"
-#include "zebra/zebra_nhg.h"
DEFINE_MTYPE_STATIC(ZEBRA, RIB_UPDATE_CTX, "Rib update context object");
@@ -79,34 +78,35 @@ static const struct {
uint8_t distance;
uint8_t meta_q_map;
} route_info[ZEBRA_ROUTE_MAX] = {
- [ZEBRA_ROUTE_SYSTEM] = {ZEBRA_ROUTE_SYSTEM, 0, 4},
- [ZEBRA_ROUTE_KERNEL] = {ZEBRA_ROUTE_KERNEL, 0, 0},
- [ZEBRA_ROUTE_CONNECT] = {ZEBRA_ROUTE_CONNECT, 0, 0},
- [ZEBRA_ROUTE_STATIC] = {ZEBRA_ROUTE_STATIC, 1, 1},
- [ZEBRA_ROUTE_RIP] = {ZEBRA_ROUTE_RIP, 120, 2},
- [ZEBRA_ROUTE_RIPNG] = {ZEBRA_ROUTE_RIPNG, 120, 2},
- [ZEBRA_ROUTE_OSPF] = {ZEBRA_ROUTE_OSPF, 110, 2},
- [ZEBRA_ROUTE_OSPF6] = {ZEBRA_ROUTE_OSPF6, 110, 2},
- [ZEBRA_ROUTE_ISIS] = {ZEBRA_ROUTE_ISIS, 115, 2},
- [ZEBRA_ROUTE_BGP] = {ZEBRA_ROUTE_BGP, 20 /* IBGP is 200. */, 3},
- [ZEBRA_ROUTE_PIM] = {ZEBRA_ROUTE_PIM, 255, 4},
- [ZEBRA_ROUTE_EIGRP] = {ZEBRA_ROUTE_EIGRP, 90, 2},
- [ZEBRA_ROUTE_NHRP] = {ZEBRA_ROUTE_NHRP, 10, 2},
- [ZEBRA_ROUTE_HSLS] = {ZEBRA_ROUTE_HSLS, 255, 4},
- [ZEBRA_ROUTE_OLSR] = {ZEBRA_ROUTE_OLSR, 255, 4},
- [ZEBRA_ROUTE_TABLE] = {ZEBRA_ROUTE_TABLE, 150, 1},
- [ZEBRA_ROUTE_LDP] = {ZEBRA_ROUTE_LDP, 150, 4},
- [ZEBRA_ROUTE_VNC] = {ZEBRA_ROUTE_VNC, 20, 3},
- [ZEBRA_ROUTE_VNC_DIRECT] = {ZEBRA_ROUTE_VNC_DIRECT, 20, 3},
- [ZEBRA_ROUTE_VNC_DIRECT_RH] = {ZEBRA_ROUTE_VNC_DIRECT_RH, 20, 3},
- [ZEBRA_ROUTE_BGP_DIRECT] = {ZEBRA_ROUTE_BGP_DIRECT, 20, 3},
- [ZEBRA_ROUTE_BGP_DIRECT_EXT] = {ZEBRA_ROUTE_BGP_DIRECT_EXT, 20, 3},
- [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 100, 2},
- [ZEBRA_ROUTE_SHARP] = {ZEBRA_ROUTE_SHARP, 150, 4},
- [ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 4},
- [ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, 4},
- [ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, 115, 2},
- [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 4}
+ [ZEBRA_ROUTE_NHG] = {ZEBRA_ROUTE_NHG, 255 /* Uneeded for nhg's */, 0},
+ [ZEBRA_ROUTE_SYSTEM] = {ZEBRA_ROUTE_SYSTEM, 0, 5},
+ [ZEBRA_ROUTE_KERNEL] = {ZEBRA_ROUTE_KERNEL, 0, 1},
+ [ZEBRA_ROUTE_CONNECT] = {ZEBRA_ROUTE_CONNECT, 0, 1},
+ [ZEBRA_ROUTE_STATIC] = {ZEBRA_ROUTE_STATIC, 1, 2},
+ [ZEBRA_ROUTE_RIP] = {ZEBRA_ROUTE_RIP, 120, 3},
+ [ZEBRA_ROUTE_RIPNG] = {ZEBRA_ROUTE_RIPNG, 120, 3},
+ [ZEBRA_ROUTE_OSPF] = {ZEBRA_ROUTE_OSPF, 110, 3},
+ [ZEBRA_ROUTE_OSPF6] = {ZEBRA_ROUTE_OSPF6, 110, 3},
+ [ZEBRA_ROUTE_ISIS] = {ZEBRA_ROUTE_ISIS, 115, 3},
+ [ZEBRA_ROUTE_BGP] = {ZEBRA_ROUTE_BGP, 20 /* IBGP is 200. */, 4},
+ [ZEBRA_ROUTE_PIM] = {ZEBRA_ROUTE_PIM, 255, 5},
+ [ZEBRA_ROUTE_EIGRP] = {ZEBRA_ROUTE_EIGRP, 90, 3},
+ [ZEBRA_ROUTE_NHRP] = {ZEBRA_ROUTE_NHRP, 10, 3},
+ [ZEBRA_ROUTE_HSLS] = {ZEBRA_ROUTE_HSLS, 255, 5},
+ [ZEBRA_ROUTE_OLSR] = {ZEBRA_ROUTE_OLSR, 255, 5},
+ [ZEBRA_ROUTE_TABLE] = {ZEBRA_ROUTE_TABLE, 150, 2},
+ [ZEBRA_ROUTE_LDP] = {ZEBRA_ROUTE_LDP, 150, 5},
+ [ZEBRA_ROUTE_VNC] = {ZEBRA_ROUTE_VNC, 20, 4},
+ [ZEBRA_ROUTE_VNC_DIRECT] = {ZEBRA_ROUTE_VNC_DIRECT, 20, 4},
+ [ZEBRA_ROUTE_VNC_DIRECT_RH] = {ZEBRA_ROUTE_VNC_DIRECT_RH, 20, 4},
+ [ZEBRA_ROUTE_BGP_DIRECT] = {ZEBRA_ROUTE_BGP_DIRECT, 20, 4},
+ [ZEBRA_ROUTE_BGP_DIRECT_EXT] = {ZEBRA_ROUTE_BGP_DIRECT_EXT, 20, 4},
+ [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 100, 3},
+ [ZEBRA_ROUTE_SHARP] = {ZEBRA_ROUTE_SHARP, 150, 5},
+ [ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 5},
+ [ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, 5},
+ [ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, 115, 3},
+ [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 5}
/* Any new route type added to zebra, should be mirrored here */
/* no entry/default: 150 */
@@ -196,8 +196,7 @@ int zebra_check_addr(const struct prefix *p)
/* Add nexthop to the end of a rib node's nexthop list */
void route_entry_nexthop_add(struct route_entry *re, struct nexthop *nexthop)
{
- _nexthop_group_add_sorted(&re->ng, nexthop);
- re->nexthop_num++;
+ _nexthop_group_add_sorted(re->ng, nexthop);
}
@@ -206,10 +205,8 @@ void route_entry_nexthop_add(struct route_entry *re, struct nexthop *nexthop)
*/
void route_entry_copy_nexthops(struct route_entry *re, struct nexthop *nh)
{
- assert(!re->ng.nexthop);
- copy_nexthops(&re->ng.nexthop, nh, NULL);
- for (struct nexthop *nexthop = nh; nexthop; nexthop = nexthop->next)
- re->nexthop_num++;
+ assert(!re->ng->nexthop);
+ copy_nexthops(&re->ng->nexthop, nh, NULL);
}
/* Delete specified nexthop from the list. */
@@ -220,8 +217,7 @@ void route_entry_nexthop_delete(struct route_entry *re, struct nexthop *nexthop)
if (nexthop->prev)
nexthop->prev->next = nexthop->next;
else
- re->ng.nexthop = nexthop->next;
- re->nexthop_num--;
+ re->ng->nexthop = nexthop->next;
}
@@ -505,7 +501,7 @@ int zebra_rib_labeled_unicast(struct route_entry *re)
if (re->type != ZEBRA_ROUTE_BGP)
return 0;
- for (ALL_NEXTHOPS(re->ng, nexthop))
+ for (ALL_NEXTHOPS_PTR(re->ng, nexthop))
if (!nexthop->nh_label || !nexthop->nh_label->num_labels)
return 0;
@@ -529,26 +525,17 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re,
srcdest_rnode_prefixes(rn, &p, &src_p);
if (info->safi != SAFI_UNICAST) {
- for (ALL_NEXTHOPS(re->ng, nexthop))
+ for (ALL_NEXTHOPS_PTR(re->ng, nexthop))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
return;
- } else {
- struct nexthop *prev;
-
- for (ALL_NEXTHOPS(re->ng, nexthop)) {
- UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_DUPLICATE);
- for (ALL_NEXTHOPS(re->ng, prev)) {
- if (prev == nexthop)
- break;
- if (nexthop_same_firsthop(nexthop, prev)) {
- SET_FLAG(nexthop->flags,
- NEXTHOP_FLAG_DUPLICATE);
- break;
- }
- }
- }
}
+
+ /*
+ * Install the resolved nexthop object first.
+ */
+ zebra_nhg_install_kernel(zebra_nhg_lookup_id(re->nhe_id));
+
/*
* If this is a replace to a new RE let the originator of the RE
* know that they've lost
@@ -586,7 +573,7 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re,
if (!RIB_SYSTEM_ROUTE(old)) {
/* Clear old route's FIB flags */
- for (ALL_NEXTHOPS(old->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(old->ng, nexthop)) {
UNSET_FLAG(nexthop->flags,
NEXTHOP_FLAG_FIB);
}
@@ -624,7 +611,7 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re)
if (info->safi != SAFI_UNICAST) {
UNSET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
- for (ALL_NEXTHOPS(re->ng, nexthop))
+ for (ALL_NEXTHOPS_PTR(re->ng, nexthop))
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
return;
}
@@ -684,7 +671,7 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re)
re->fib_ng.nexthop = NULL;
}
- for (ALL_NEXTHOPS(re->ng, nexthop))
+ for (ALL_NEXTHOPS_PTR(re->ng, nexthop))
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
}
@@ -860,7 +847,7 @@ static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn,
/* Update real nexthop. This may actually determine if nexthop is active
* or not. */
- if (!nexthop_group_active_nexthop_num(&new->ng)) {
+ if (!nexthop_group_active_nexthop_num(new->ng)) {
UNSET_FLAG(new->status, ROUTE_ENTRY_CHANGED);
return;
}
@@ -929,7 +916,7 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf,
/* Update the nexthop; we could determine here that nexthop is
* inactive. */
- if (nexthop_group_active_nexthop_num(&new->ng))
+ if (nexthop_group_active_nexthop_num(new->ng))
nh_active = 1;
/* If nexthop is active, install the selected route, if
@@ -1047,7 +1034,7 @@ static struct route_entry *rib_choose_best(struct route_entry *current,
/* both are connected. are either loop or vrf? */
struct nexthop *nexthop = NULL;
- for (ALL_NEXTHOPS(alternate->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(alternate->ng, nexthop)) {
struct interface *ifp = if_lookup_by_index(
nexthop->ifindex, alternate->vrf_id);
@@ -1055,7 +1042,7 @@ static struct route_entry *rib_choose_best(struct route_entry *current,
return alternate;
}
- for (ALL_NEXTHOPS(current->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(current->ng, nexthop)) {
struct interface *ifp = if_lookup_by_index(
nexthop->ifindex, current->vrf_id);
@@ -1086,6 +1073,12 @@ static struct route_entry *rib_choose_best(struct route_entry *current,
return current;
}
+/* Core function for processing nexthop group contexts's off metaq */
+static void rib_nhg_process(struct nhg_ctx *ctx)
+{
+ nhg_ctx_process(ctx);
+}
+
/* Core function for processing routing information base. */
static void rib_process(struct route_node *rn)
{
@@ -1380,7 +1373,7 @@ static void zebra_rib_fixup_system(struct route_node *rn)
SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
UNSET_FLAG(re->status, ROUTE_ENTRY_QUEUED);
- for (ALL_NEXTHOPS(re->ng, nhop)) {
+ for (ALL_NEXTHOPS_PTR(re->ng, nhop)) {
if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_RECURSIVE))
continue;
@@ -1428,76 +1421,20 @@ static bool rib_update_re_from_ctx(struct route_entry *re,
* status.
*/
- /*
- * First check the fib nexthop-group, if it's present. The comparison
- * here is quite strict: we require that the fib sets match exactly.
+ /* Check both fib group and notif group for equivalence.
+ *
+ * Let's assume the nexthops are ordered here to save time.
*/
- matched = false;
- do {
- if (re->fib_ng.nexthop == NULL)
- break;
-
- matched = true;
-
- /* First check the route's fib nexthops */
- for (ALL_NEXTHOPS(re->fib_ng, nexthop)) {
-
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
- continue;
-
- ctx_nexthop = NULL;
- for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx),
- ctx_nexthop)) {
- if (nexthop_same(ctx_nexthop, nexthop))
- break;
- }
-
- if (ctx_nexthop == NULL) {
- /* Nexthop not in the new installed set */
- if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
- nexthop2str(nexthop, nh_str,
- sizeof(nh_str));
- zlog_debug("update_from_ctx: no match for fib nh %s",
- nh_str);
- }
-
- matched = false;
- break;
- }
- }
-
- if (!matched)
- break;
-
- /* Check the new installed set */
- ctx_nexthop = NULL;
- for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), ctx_nexthop)) {
-
- if (CHECK_FLAG(ctx_nexthop->flags,
- NEXTHOP_FLAG_RECURSIVE))
- continue;
-
- /* Compare with the current group's nexthops */
- nexthop = NULL;
- for (ALL_NEXTHOPS(re->fib_ng, nexthop)) {
- if (nexthop_same(nexthop, ctx_nexthop))
- break;
- }
-
- if (nexthop == NULL) {
- /* Nexthop not in the old installed set */
- if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
- nexthop2str(ctx_nexthop, nh_str,
- sizeof(nh_str));
- zlog_debug("update_from_ctx: no fib match for notif nh %s",
- nh_str);
- }
- matched = false;
- break;
- }
+ if (nexthop_group_equal(&re->fib_ng, dplane_ctx_get_ng(ctx)) == false) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
+ zlog_debug(
+ "%u:%s update_from_ctx: notif nh and fib nh mismatch",
+ re->vrf_id, dest_str);
}
- } while (0);
+ matched = false;
+ } else
+ matched = true;
/* If the new FIB set matches the existing FIB set, we're done. */
if (matched) {
@@ -1530,9 +1467,22 @@ static bool rib_update_re_from_ctx(struct route_entry *re,
* walk the RIB group, looking for the 'installable' candidate
* nexthops, and then check those against the set
* that is actually installed.
+ *
+ * Assume nexthops are ordered here as well.
*/
matched = true;
- for (ALL_NEXTHOPS(re->ng, nexthop)) {
+
+ ctx_nexthop = dplane_ctx_get_ng(ctx)->nexthop;
+
+ /* Get the first `installed` one to check against.
+ * If the dataplane doesn't set these to be what was actually installed,
+ * it will just be whatever was in re->ng?
+ */
+ if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_RECURSIVE)
+ || !CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+ ctx_nexthop = nexthop_next_active_resolved(ctx_nexthop);
+
+ for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
continue;
@@ -1541,20 +1491,15 @@ static bool rib_update_re_from_ctx(struct route_entry *re,
continue;
/* Check for a FIB nexthop corresponding to the RIB nexthop */
- ctx_nexthop = NULL;
- for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), ctx_nexthop)) {
- if (nexthop_same(ctx_nexthop, nexthop))
- break;
- }
-
- /* If the FIB doesn't know about the nexthop,
- * it's not installed
- */
- if (ctx_nexthop == NULL) {
+ if (nexthop_same(ctx_nexthop, nexthop) == false) {
+ /* If the FIB doesn't know about the nexthop,
+ * it's not installed
+ */
if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
nexthop2str(nexthop, nh_str, sizeof(nh_str));
- zlog_debug("update_from_ctx: no notif match for rib nh %s",
- nh_str);
+ zlog_debug(
+ "update_from_ctx: no notif match for rib nh %s",
+ nh_str);
}
matched = false;
@@ -1578,6 +1523,8 @@ static bool rib_update_re_from_ctx(struct route_entry *re,
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
}
+
+ ctx_nexthop = nexthop_next_active_resolved(ctx_nexthop);
}
/* If all nexthops were processed, we're done */
@@ -2062,19 +2009,28 @@ done:
dplane_ctx_fini(&ctx);
}
-/* Take a list of route_node structs and return 1, if there was a record
- * picked from it and processed by rib_process(). Don't process more,
- * than one RN record; operate only in the specified sub-queue.
- */
-static unsigned int process_subq(struct list *subq, uint8_t qindex)
+static void process_subq_nhg(struct listnode *lnode)
{
- struct listnode *lnode = listhead(subq);
- struct route_node *rnode;
- rib_dest_t *dest;
- struct zebra_vrf *zvrf = NULL;
+ struct nhg_ctx *ctx = NULL;
+ uint8_t qindex = route_info[ZEBRA_ROUTE_NHG].meta_q_map;
- if (!lnode)
- return 0;
+ ctx = listgetdata(lnode);
+
+ if (!ctx)
+ return;
+
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("NHG Context id=%u dequeued from sub-queue %u",
+ ctx->id, qindex);
+
+ rib_nhg_process(ctx);
+}
+
+static void process_subq_route(struct listnode *lnode, uint8_t qindex)
+{
+ struct route_node *rnode = NULL;
+ rib_dest_t *dest = NULL;
+ struct zebra_vrf *zvrf = NULL;
rnode = listgetdata(lnode);
dest = rib_dest_from_rnode(rnode);
@@ -2104,7 +2060,26 @@ static unsigned int process_subq(struct list *subq, uint8_t qindex)
}
#endif
route_unlock_node(rnode);
+}
+
+/* Take a list of route_node structs and return 1, if there was a record
+ * picked from it and processed by rib_process(). Don't process more,
+ * than one RN record; operate only in the specified sub-queue.
+ */
+static unsigned int process_subq(struct list *subq, uint8_t qindex)
+{
+ struct listnode *lnode = listhead(subq);
+
+ if (!lnode)
+ return 0;
+
+ if (qindex == route_info[ZEBRA_ROUTE_NHG].meta_q_map)
+ process_subq_nhg(lnode);
+ else
+ process_subq_route(lnode, qindex);
+
list_delete_node(subq, lnode);
+
return 1;
}
@@ -2162,11 +2137,14 @@ static wq_item_status meta_queue_process(struct work_queue *dummy, void *data)
* original metaqueue index value will win and we'll end up with
* the route node enqueued once.
*/
-static void rib_meta_queue_add(struct meta_queue *mq, struct route_node *rn)
+static int rib_meta_queue_add(struct meta_queue *mq, void *data)
{
+ struct route_node *rn = NULL;
struct route_entry *re = NULL, *curr_re = NULL;
uint8_t qindex = MQ_SIZE, curr_qindex = MQ_SIZE;
+ rn = (struct route_node *)data;
+
RNODE_FOREACH_RE (rn, curr_re) {
curr_qindex = route_info[curr_re->type].meta_q_map;
@@ -2177,7 +2155,7 @@ static void rib_meta_queue_add(struct meta_queue *mq, struct route_node *rn)
}
if (!re)
- return;
+ return -1;
/* Invariant: at this point we always have rn->info set. */
if (CHECK_FLAG(rib_dest_from_rnode(rn)->flags,
@@ -2186,7 +2164,7 @@ static void rib_meta_queue_add(struct meta_queue *mq, struct route_node *rn)
rnode_debug(rn, re->vrf_id,
"rn %p is already queued in sub-queue %u",
(void *)rn, qindex);
- return;
+ return -1;
}
SET_FLAG(rib_dest_from_rnode(rn)->flags, RIB_ROUTE_QUEUED(qindex));
@@ -2197,26 +2175,37 @@ static void rib_meta_queue_add(struct meta_queue *mq, struct route_node *rn)
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
rnode_debug(rn, re->vrf_id, "queued rn %p into sub-queue %u",
(void *)rn, qindex);
+
+ return 0;
}
-/* Add route_node to work queue and schedule processing */
-void rib_queue_add(struct route_node *rn)
+static int rib_meta_queue_nhg_add(struct meta_queue *mq, void *data)
{
- assert(rn);
+ struct nhg_ctx *ctx = NULL;
+ uint8_t qindex = route_info[ZEBRA_ROUTE_NHG].meta_q_map;
- /* Pointless to queue a route_node with no RIB entries to add or remove
- */
- if (!rnode_to_ribs(rn)) {
- zlog_debug("%s: called for route_node (%p, %d) with no ribs",
- __func__, (void *)rn, rn->lock);
- zlog_backtrace(LOG_DEBUG);
- return;
- }
+ ctx = (struct nhg_ctx *)data;
+
+ if (!ctx)
+ return -1;
+ listnode_add(mq->subq[qindex], ctx);
+ mq->size++;
+
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("NHG Context id=%u queued into sub-queue %u",
+ ctx->id, qindex);
+
+ return 0;
+}
+
+static int mq_add_handler(void *data,
+ int (*mq_add_func)(struct meta_queue *mq, void *data))
+{
if (zrouter.ribq == NULL) {
flog_err(EC_ZEBRA_WQ_NONEXISTENT,
"%s: work_queue does not exist!", __func__);
- return;
+ return -1;
}
/*
@@ -2230,9 +2219,31 @@ void rib_queue_add(struct route_node *rn)
if (work_queue_empty(zrouter.ribq))
work_queue_add(zrouter.ribq, zrouter.mq);
- rib_meta_queue_add(zrouter.mq, rn);
+ return mq_add_func(zrouter.mq, data);
+}
- return;
+/* Add route_node to work queue and schedule processing */
+int rib_queue_add(struct route_node *rn)
+{
+ assert(rn);
+
+ /* Pointless to queue a route_node with no RIB entries to add or remove
+ */
+ if (!rnode_to_ribs(rn)) {
+ zlog_debug("%s: called for route_node (%p, %d) with no ribs",
+ __func__, (void *)rn, rn->lock);
+ zlog_backtrace(LOG_DEBUG);
+ return -1;
+ }
+
+ return mq_add_handler(rn, &rib_meta_queue_add);
+}
+
+int rib_queue_nhg_add(struct nhg_ctx *ctx)
+{
+ assert(ctx);
+
+ return mq_add_handler(ctx, &rib_meta_queue_nhg_add);
}
/* Create new meta queue.
@@ -2400,6 +2411,7 @@ static void rib_addnode(struct route_node *rn,
void rib_unlink(struct route_node *rn, struct route_entry *re)
{
rib_dest_t *dest;
+ struct nhg_hash_entry *nhe = NULL;
assert(rn && re);
@@ -2414,7 +2426,13 @@ void rib_unlink(struct route_node *rn, struct route_entry *re)
if (dest->selected_fib == re)
dest->selected_fib = NULL;
- nexthops_free(re->ng.nexthop);
+ if (re->nhe_id) {
+ nhe = zebra_nhg_lookup_id(re->nhe_id);
+ if (nhe)
+ zebra_nhg_decrement_ref(nhe);
+ } else if (re->ng)
+ nexthop_group_delete(&re->ng);
+
nexthops_free(re->fib_ng.nexthop);
XFREE(MTYPE_RE, re);
@@ -2480,9 +2498,10 @@ void _route_entry_dump(const char *func, union prefixconstptr pp,
"%s: metric == %u, mtu == %u, distance == %u, flags == %u, status == %u",
straddr, re->metric, re->mtu, re->distance, re->flags, re->status);
zlog_debug("%s: nexthop_num == %u, nexthop_active_num == %u", straddr,
- re->nexthop_num, re->nexthop_active_num);
+ nexthop_group_nexthop_num(re->ng),
+ nexthop_group_active_nexthop_num(re->ng));
- for (ALL_NEXTHOPS(re->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
struct interface *ifp;
struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id);
@@ -2633,6 +2652,7 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id)
int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
struct prefix_ipv6 *src_p, struct route_entry *re)
{
+ struct nhg_hash_entry *nhe = NULL;
struct route_table *table;
struct route_node *rn;
struct route_entry *same = NULL;
@@ -2646,10 +2666,58 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
/* Lookup table. */
table = zebra_vrf_table_with_table_id(afi, safi, re->vrf_id, re->table);
if (!table) {
+ if (re->ng)
+ nexthop_group_delete(&re->ng);
XFREE(MTYPE_RE, re);
return 0;
}
+ if (re->nhe_id) {
+ nhe = zebra_nhg_lookup_id(re->nhe_id);
+
+ if (!nhe) {
+ flog_err(
+ EC_ZEBRA_TABLE_LOOKUP_FAILED,
+ "Zebra failed to find the nexthop hash entry for id=%u in a route entry",
+ re->nhe_id);
+ XFREE(MTYPE_RE, re);
+ return -1;
+ }
+ } else {
+ nhe = zebra_nhg_rib_find(0, re->ng, afi);
+
+ /*
+ * The nexthops got copied over into an nhe,
+ * so free them now.
+ */
+ nexthop_group_delete(&re->ng);
+
+ if (!nhe) {
+ char buf[PREFIX_STRLEN] = "";
+ char buf2[PREFIX_STRLEN] = "";
+
+ flog_err(
+ EC_ZEBRA_TABLE_LOOKUP_FAILED,
+ "Zebra failed to find or create a nexthop hash entry for %s%s%s",
+ prefix2str(p, buf, sizeof(buf)),
+ src_p ? " from " : "",
+ src_p ? prefix2str(src_p, buf2, sizeof(buf2))
+ : "");
+
+ XFREE(MTYPE_RE, re);
+ return -1;
+ }
+ }
+
+ /*
+ * Attach the re to the nhe's nexthop group.
+ *
+ * TODO: This will need to change when we start getting IDs from upper
+ * level protocols, as the refcnt might be wrong, since it checks
+ * if old_id != new_id.
+ */
+ zebra_nhg_re_update_ref(re, nhe);
+
/* Make it sure prefixlen is applied to the prefix. */
apply_mask(p);
if (src_p)
@@ -2726,8 +2794,8 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
unsigned short instance, int flags, struct prefix *p,
struct prefix_ipv6 *src_p, const struct nexthop *nh,
- uint32_t table_id, uint32_t metric, uint8_t distance,
- bool fromkernel)
+ uint32_t nhe_id, uint32_t table_id, uint32_t metric,
+ uint8_t distance, bool fromkernel)
{
struct route_table *table;
struct route_node *rn;
@@ -2790,31 +2858,37 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
if (re->type == ZEBRA_ROUTE_KERNEL && re->metric != metric)
continue;
- if (re->type == ZEBRA_ROUTE_CONNECT && (rtnh = re->ng.nexthop)
+ if (re->type == ZEBRA_ROUTE_CONNECT && (rtnh = re->ng->nexthop)
&& rtnh->type == NEXTHOP_TYPE_IFINDEX && nh) {
if (rtnh->ifindex != nh->ifindex)
continue;
same = re;
break;
}
+
/* Make sure that the route found has the same gateway. */
- else {
- if (nh == NULL) {
+ if (nhe_id && re->nhe_id == nhe_id) {
+ same = re;
+ break;
+ }
+
+ if (nh == NULL) {
+ same = re;
+ break;
+ }
+ for (ALL_NEXTHOPS_PTR(re->ng, rtnh)) {
+ /*
+ * No guarantee all kernel send nh with labels
+ * on delete.
+ */
+ if (nexthop_same_no_labels(rtnh, nh)) {
same = re;
break;
}
- for (ALL_NEXTHOPS(re->ng, rtnh))
- /*
- * No guarantee all kernel send nh with labels
- * on delete.
- */
- if (nexthop_same_no_labels(rtnh, nh)) {
- same = re;
- break;
- }
- if (same)
- break;
}
+
+ if (same)
+ break;
}
/* If same type of route can't be found and this message is from
kernel. */
@@ -2844,7 +2918,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
if (allow_delete) {
UNSET_FLAG(fib->status, ROUTE_ENTRY_INSTALLED);
/* Unset flags. */
- for (rtnh = fib->ng.nexthop; rtnh;
+ for (rtnh = fib->ng->nexthop; rtnh;
rtnh = rtnh->next)
UNSET_FLAG(rtnh->flags,
NEXTHOP_FLAG_FIB);
@@ -2900,7 +2974,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
if (CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE)) {
struct nexthop *tmp_nh;
- for (ALL_NEXTHOPS(re->ng, tmp_nh)) {
+ for (ALL_NEXTHOPS_PTR(re->ng, tmp_nh)) {
struct ipaddr vtep_ip;
memset(&vtep_ip, 0, sizeof(struct ipaddr));
@@ -2935,11 +3009,11 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
unsigned short instance, int flags, struct prefix *p,
struct prefix_ipv6 *src_p, const struct nexthop *nh,
- uint32_t table_id, uint32_t metric, uint32_t mtu, uint8_t distance,
- route_tag_t tag)
+ uint32_t nhe_id, uint32_t table_id, uint32_t metric, uint32_t mtu,
+ uint8_t distance, route_tag_t tag)
{
- struct route_entry *re;
- struct nexthop *nexthop;
+ struct route_entry *re = NULL;
+ struct nexthop *nexthop = NULL;
/* Allocate new route_entry structure. */
re = XCALLOC(MTYPE_RE, sizeof(struct route_entry));
@@ -2951,14 +3025,18 @@ int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
re->mtu = mtu;
re->table = table_id;
re->vrf_id = vrf_id;
- re->nexthop_num = 0;
re->uptime = monotime(NULL);
re->tag = tag;
+ re->nhe_id = nhe_id;
- /* Add nexthop. */
- nexthop = nexthop_new();
- *nexthop = *nh;
- route_entry_nexthop_add(re, nexthop);
+ if (!nhe_id) {
+ re->ng = nexthop_group_new();
+
+ /* Add nexthop. */
+ nexthop = nexthop_new();
+ *nexthop = *nh;
+ route_entry_nexthop_add(re, nexthop);
+ }
return rib_add_multipath(afi, safi, p, src_p, re);
}
@@ -3218,7 +3296,7 @@ void rib_sweep_table(struct route_table *table)
* this decision needs to be revisited
*/
SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
- for (ALL_NEXTHOPS(re->ng, nexthop))
+ for (ALL_NEXTHOPS_PTR(re->ng, nexthop))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
rib_uninstall_kernel(rn, re);
@@ -3242,6 +3320,7 @@ int rib_sweep_route(struct thread *t)
}
zebra_router_sweep_route();
+ zebra_router_sweep_nhgs();
return 0;
}
@@ -3412,6 +3491,12 @@ static int rib_process_dplane_results(struct thread *thread)
rib_process_dplane_notify(ctx);
break;
+ case DPLANE_OP_NH_INSTALL:
+ case DPLANE_OP_NH_UPDATE:
+ case DPLANE_OP_NH_DELETE:
+ zebra_nhg_dplane_result(ctx);
+ break;
+
case DPLANE_OP_LSP_INSTALL:
case DPLANE_OP_LSP_UPDATE:
case DPLANE_OP_LSP_DELETE:
diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c
index 5df5d94f4b..60e23cc4d4 100644
--- a/zebra/zebra_rnh.c
+++ b/zebra/zebra_rnh.c
@@ -384,7 +384,7 @@ static void zebra_rnh_clear_nexthop_rnh_filters(struct route_entry *re)
struct nexthop *nexthop;
if (re) {
- for (nexthop = re->ng.nexthop; nexthop;
+ for (nexthop = re->ng->nexthop; nexthop;
nexthop = nexthop->next) {
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RNH_FILTERED);
}
@@ -403,7 +403,7 @@ static int zebra_rnh_apply_nht_rmap(afi_t afi, struct zebra_vrf *zvrf,
route_map_result_t ret;
if (prn && re) {
- for (nexthop = re->ng.nexthop; nexthop;
+ for (nexthop = re->ng->nexthop; nexthop;
nexthop = nexthop->next) {
ret = zebra_nht_route_map_check(
afi, proto, &prn->p, zvrf, re, nexthop);
@@ -688,7 +688,7 @@ zebra_rnh_resolve_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi,
/* Just being SELECTED isn't quite enough - must
* have an installed nexthop to be useful.
*/
- for (ALL_NEXTHOPS(re->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
if (rnh_nexthop_valid(re, nexthop))
break;
}
@@ -707,7 +707,7 @@ zebra_rnh_resolve_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi,
break;
if (re->type == ZEBRA_ROUTE_NHRP) {
- for (nexthop = re->ng.nexthop; nexthop;
+ for (nexthop = re->ng->nexthop; nexthop;
nexthop = nexthop->next)
if (nexthop->type
== NEXTHOP_TYPE_IFINDEX)
@@ -940,7 +940,7 @@ static void free_state(vrf_id_t vrf_id, struct route_entry *re,
return;
/* free RE and nexthops */
- nexthops_free(re->ng.nexthop);
+ nexthop_group_delete(&re->ng);
XFREE(MTYPE_RE, re);
}
@@ -963,8 +963,9 @@ static void copy_state(struct rnh *rnh, struct route_entry *re,
state->metric = re->metric;
state->vrf_id = re->vrf_id;
state->status = re->status;
+ state->ng = nexthop_group_new();
- route_entry_copy_nexthops(state, re->ng.nexthop);
+ route_entry_copy_nexthops(state, re->ng->nexthop);
rnh->state = state;
}
@@ -982,10 +983,11 @@ static int compare_state(struct route_entry *r1, struct route_entry *r2)
if (r1->metric != r2->metric)
return 1;
- if (r1->nexthop_num != r2->nexthop_num)
+ if (nexthop_group_nexthop_num(r1->ng)
+ != nexthop_group_nexthop_num(r2->ng))
return 1;
- if (nexthop_group_hash(&r1->ng) != nexthop_group_hash(&r2->ng))
+ if (nexthop_group_hash(r1->ng) != nexthop_group_hash(r2->ng))
return 1;
return 0;
@@ -1035,7 +1037,7 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type,
num = 0;
nump = stream_get_endp(s);
stream_putc(s, 0);
- for (ALL_NEXTHOPS(re->ng, nh))
+ for (ALL_NEXTHOPS_PTR(re->ng, nh))
if (rnh_nexthop_valid(re, nh)) {
stream_putl(s, nh->vrf_id);
stream_putc(s, nh->type);
@@ -1135,7 +1137,7 @@ static void print_rnh(struct route_node *rn, struct vty *vty)
if (rnh->state) {
vty_out(vty, " resolved via %s\n",
zebra_route_string(rnh->state->type));
- for (nexthop = rnh->state->ng.nexthop; nexthop;
+ for (nexthop = rnh->state->ng->nexthop; nexthop;
nexthop = nexthop->next)
print_nh(nexthop, vty);
} else
diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c
index 1e9f9e4ec7..e5319c64af 100644
--- a/zebra/zebra_router.c
+++ b/zebra/zebra_router.c
@@ -29,7 +29,7 @@
#include "zebra_pbr.h"
#include "zebra_vxlan.h"
#include "zebra_mlag.h"
-#include "zebra_nhg.h"
+#include "zebra_nhg_private.h"
#include "debug.h"
DEFINE_MTYPE_STATIC(ZEBRA, RIB_TABLE_INFO, "RIB table info")
@@ -154,6 +154,11 @@ void zebra_router_sweep_route(void)
}
}
+void zebra_router_sweep_nhgs(void)
+{
+ zebra_nhg_sweep_table(zrouter.nhgs_id);
+}
+
static void zebra_router_free_table(struct zebra_router_table *zrt)
{
void *table_info;
@@ -218,6 +223,11 @@ void zebra_router_terminate(void)
zebra_vxlan_disable();
zebra_mlag_terminate();
+ hash_clean(zrouter.nhgs, zebra_nhg_free);
+ hash_free(zrouter.nhgs);
+ hash_clean(zrouter.nhgs_id, NULL);
+ hash_free(zrouter.nhgs_id);
+
hash_clean(zrouter.rules_hash, zebra_pbr_rules_free);
hash_free(zrouter.rules_hash);
@@ -253,4 +263,11 @@ void zebra_router_init(void)
zrouter.iptable_hash = hash_create_size(8, zebra_pbr_iptable_hash_key,
zebra_pbr_iptable_hash_equal,
"IPtable Hash Entry");
+
+ zrouter.nhgs =
+ hash_create_size(8, zebra_nhg_hash_key, zebra_nhg_hash_equal,
+ "Zebra Router Nexthop Groups");
+ zrouter.nhgs_id =
+ hash_create_size(8, zebra_nhg_id_key, zebra_nhg_hash_id_equal,
+ "Zebra Router Nexthop Groups ID index");
}
diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h
index 25a7adac11..ac4c961475 100644
--- a/zebra/zebra_router.h
+++ b/zebra/zebra_router.h
@@ -132,6 +132,12 @@ struct zebra_router {
* Time for when we sweep the rib from old routes
*/
time_t startup_time;
+
+ /*
+ * The hash of nexthop groups associated with this router
+ */
+ struct hash *nhgs;
+ struct hash *nhgs_id;
};
#define GRACEFUL_RESTART_TIME 60
@@ -139,6 +145,7 @@ struct zebra_router {
extern struct zebra_router zrouter;
extern void zebra_router_init(void);
+extern void zebra_router_cleanup(void);
extern void zebra_router_terminate(void);
extern struct route_table *zebra_router_find_table(struct zebra_vrf *zvrf,
@@ -153,6 +160,7 @@ extern void zebra_router_release_table(struct zebra_vrf *zvrf, uint32_t tableid,
extern int zebra_router_config_write(struct vty *vty);
extern void zebra_router_sweep_route(void);
+extern void zebra_router_sweep_nhgs(void);
extern void zebra_router_show_table_summary(struct vty *vty);
diff --git a/zebra/zebra_snmp.c b/zebra/zebra_snmp.c
index 74eab765c8..56c7664328 100644
--- a/zebra/zebra_snmp.c
+++ b/zebra/zebra_snmp.c
@@ -285,8 +285,8 @@ static void check_replace(struct route_node *np2, struct route_entry *re2,
return;
}
- if (in_addr_cmp((uint8_t *)&(*re)->ng.nexthop->gate.ipv4,
- (uint8_t *)&re2->ng.nexthop->gate.ipv4)
+ if (in_addr_cmp((uint8_t *)&(*re)->ng->nexthop->gate.ipv4,
+ (uint8_t *)&re2->ng->nexthop->gate.ipv4)
<= 0)
return;
@@ -372,7 +372,7 @@ static void get_fwtable_route_node(struct variable *v, oid objid[],
(uint8_t *)&dest)) {
RNODE_FOREACH_RE (*np, *re) {
if (!in_addr_cmp((uint8_t *)&(*re)
- ->ng.nexthop
+ ->ng->nexthop
->gate.ipv4,
(uint8_t *)&nexthop))
if (proto
@@ -406,7 +406,7 @@ static void get_fwtable_route_node(struct variable *v, oid objid[],
|| ((policy == policy2) && (proto < proto2))
|| ((policy == policy2) && (proto == proto2)
&& (in_addr_cmp(
- (uint8_t *)&re2->ng.nexthop
+ (uint8_t *)&re2->ng->nexthop
->gate.ipv4,
(uint8_t *)&nexthop)
>= 0)))
@@ -432,7 +432,7 @@ static void get_fwtable_route_node(struct variable *v, oid objid[],
{
struct nexthop *nexthop;
- nexthop = (*re)->ng.nexthop;
+ nexthop = (*re)->ng->nexthop;
if (nexthop) {
pnt = (uint8_t *)&nexthop->gate.ipv4;
for (i = 0; i < 4; i++)
@@ -462,7 +462,7 @@ static uint8_t *ipFwTable(struct variable *v, oid objid[], size_t *objid_len,
if (!np)
return NULL;
- nexthop = re->ng.nexthop;
+ nexthop = re->ng->nexthop;
if (!nexthop)
return NULL;
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index 92f8dd1ecc..9d17454730 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -52,6 +52,8 @@
#include "zebra/ipforward.h"
#include "zebra/zebra_vxlan_private.h"
#include "zebra/zebra_pbr.h"
+#include "zebra/zebra_nhg.h"
+#include "zebra/interface.h"
extern int allow_delete;
@@ -62,7 +64,7 @@ static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
bool supernets_only, int type,
unsigned short ospf_instance_id);
static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
- int mcast, bool use_fib);
+ int mcast, bool use_fib, bool show_ng);
static void vty_show_ip_route_summary(struct vty *vty,
struct route_table *table);
static void vty_show_ip_route_summary_prefix(struct vty *vty,
@@ -154,7 +156,7 @@ DEFUN (show_ip_rpf_addr,
re = rib_match_ipv4_multicast(VRF_DEFAULT, addr, &rn);
if (re)
- vty_show_ip_route_detail(vty, rn, 1, false);
+ vty_show_ip_route_detail(vty, rn, 1, false, false);
else
vty_out(vty, "%% No match for RPF lookup\n");
@@ -186,7 +188,7 @@ static char re_status_output_char(struct route_entry *re, struct nexthop *nhop)
/* New RIB. Detailed information for IPv4 route. */
static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
- int mcast, bool use_fib)
+ int mcast, bool use_fib, bool show_ng)
{
struct route_entry *re;
struct nexthop *nexthop;
@@ -258,7 +260,10 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
tm->tm_hour);
vty_out(vty, " ago\n");
- for (ALL_NEXTHOPS(re->ng, nexthop)) {
+ if (show_ng)
+ vty_out(vty, " Nexthop Group ID: %u\n", re->nhe_id);
+
+ for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
char addrstr[32];
vty_out(vty, " %c%s",
@@ -408,7 +413,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
if (is_fib)
nhg = rib_active_nhg(re);
else
- nhg = &(re->ng);
+ nhg = re->ng;
if (json) {
json_route = json_object_new_object();
@@ -461,9 +466,9 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
json_object_int_add(json_route, "internalFlags",
re->flags);
json_object_int_add(json_route, "internalNextHopNum",
- re->nexthop_num);
+ nexthop_group_nexthop_num(re->ng));
json_object_int_add(json_route, "internalNextHopActiveNum",
- re->nexthop_active_num);
+ nexthop_group_active_nexthop_num(re->ng));
if (uptime < ONE_DAY_SECOND)
sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min,
tm->tm_sec);
@@ -1101,6 +1106,295 @@ DEFUN (ip_nht_default_route,
return CMD_SUCCESS;
}
+static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe)
+{
+ struct nexthop *nexthop = NULL;
+ struct nhg_connected *rb_node_dep = NULL;
+ char buf[SRCDEST2STR_BUFFER];
+
+ struct vrf *nhe_vrf = vrf_lookup_by_id(nhe->vrf_id);
+
+ vty_out(vty, "ID: %u\n", nhe->id);
+ vty_out(vty, " RefCnt: %d\n", nhe->refcnt);
+
+ if (nhe_vrf)
+ vty_out(vty, " VRF: %s\n", nhe_vrf->name);
+ else
+ vty_out(vty, " VRF: UNKNOWN\n");
+
+ if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_UNHASHABLE))
+ vty_out(vty, " Duplicate - from kernel not hashable\n");
+
+ if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_VALID)) {
+ vty_out(vty, " Valid");
+ if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED))
+ vty_out(vty, ", Installed");
+ vty_out(vty, "\n");
+ }
+ if (nhe->ifp)
+ vty_out(vty, " Interface Index: %d\n", nhe->ifp->ifindex);
+
+ if (!zebra_nhg_depends_is_empty(nhe)) {
+ vty_out(vty, " Depends:");
+ frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) {
+ vty_out(vty, " (%u)", rb_node_dep->nhe->id);
+ }
+ vty_out(vty, "\n");
+ }
+
+ for (ALL_NEXTHOPS_PTR(nhe->nhg, nexthop)) {
+ if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+ vty_out(vty, " ");
+ else
+ /* Make recursive nexthops a bit more clear */
+ vty_out(vty, " ");
+
+ switch (nexthop->type) {
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ vty_out(vty, " %s", inet_ntoa(nexthop->gate.ipv4));
+ if (nexthop->ifindex)
+ vty_out(vty, ", %s",
+ ifindex2ifname(nexthop->ifindex,
+ nexthop->vrf_id));
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ vty_out(vty, " %s",
+ inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf,
+ sizeof(buf)));
+ if (nexthop->ifindex)
+ vty_out(vty, ", %s",
+ ifindex2ifname(nexthop->ifindex,
+ nexthop->vrf_id));
+ break;
+
+ case NEXTHOP_TYPE_IFINDEX:
+ vty_out(vty, " directly connected %s",
+ ifindex2ifname(nexthop->ifindex,
+ nexthop->vrf_id));
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ vty_out(vty, " unreachable");
+ switch (nexthop->bh_type) {
+ case BLACKHOLE_REJECT:
+ vty_out(vty, " (ICMP unreachable)");
+ break;
+ case BLACKHOLE_ADMINPROHIB:
+ vty_out(vty, " (ICMP admin-prohibited)");
+ break;
+ case BLACKHOLE_NULL:
+ vty_out(vty, " (blackhole)");
+ break;
+ case BLACKHOLE_UNSPEC:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id);
+
+ if (vrf)
+ vty_out(vty, " (vrf %s)", vrf->name);
+ else
+ vty_out(vty, " (vrf UNKNOWN)");
+
+ if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+ vty_out(vty, " inactive");
+
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK))
+ vty_out(vty, " onlink");
+
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+ vty_out(vty, " (recursive)");
+
+ switch (nexthop->type) {
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ if (nexthop->src.ipv4.s_addr) {
+ if (inet_ntop(AF_INET, &nexthop->src.ipv4, buf,
+ sizeof(buf)))
+ vty_out(vty, ", src %s", buf);
+ }
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any)) {
+ if (inet_ntop(AF_INET6, &nexthop->src.ipv6, buf,
+ sizeof(buf)))
+ vty_out(vty, ", src %s", buf);
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* Label information */
+ if (nexthop->nh_label && nexthop->nh_label->num_labels) {
+ vty_out(vty, ", label %s",
+ mpls_label2str(nexthop->nh_label->num_labels,
+ nexthop->nh_label->label, buf,
+ sizeof(buf), 1));
+ }
+
+ vty_out(vty, "\n");
+ }
+
+ if (!zebra_nhg_dependents_is_empty(nhe)) {
+ vty_out(vty, " Dependents:");
+ frr_each(nhg_connected_tree, &nhe->nhg_dependents,
+ rb_node_dep) {
+ vty_out(vty, " (%u)", rb_node_dep->nhe->id);
+ }
+ vty_out(vty, "\n");
+ }
+
+}
+
+static int show_nexthop_group_id_cmd_helper(struct vty *vty, uint32_t id)
+{
+ struct nhg_hash_entry *nhe = NULL;
+
+ nhe = zebra_nhg_lookup_id(id);
+
+ if (nhe)
+ show_nexthop_group_out(vty, nhe);
+ else {
+ vty_out(vty, "Nexthop Group ID: %u does not exist\n", id);
+ return CMD_WARNING;
+ }
+ return CMD_SUCCESS;
+}
+
+static void show_nexthop_group_cmd_helper(struct vty *vty,
+ struct zebra_vrf *zvrf, afi_t afi)
+{
+ struct list *list = hash_to_list(zrouter.nhgs);
+ struct nhg_hash_entry *nhe = NULL;
+ struct listnode *node = NULL;
+
+ for (ALL_LIST_ELEMENTS_RO(list, node, nhe)) {
+
+ if (afi && nhe->afi != afi)
+ continue;
+
+ if (nhe->vrf_id != zvrf->vrf->vrf_id)
+ continue;
+
+ show_nexthop_group_out(vty, nhe);
+ }
+
+ list_delete(&list);
+}
+
+static void if_nexthop_group_dump_vty(struct vty *vty, struct interface *ifp)
+{
+ struct zebra_if *zebra_if = NULL;
+ struct nhg_connected *rb_node_dep = NULL;
+
+ zebra_if = ifp->info;
+
+ if (!if_nhg_dependents_is_empty(ifp)) {
+ vty_out(vty, "Interface %s:\n", ifp->name);
+
+ frr_each(nhg_connected_tree, &zebra_if->nhg_dependents,
+ rb_node_dep) {
+ vty_out(vty, " ");
+ show_nexthop_group_out(vty, rb_node_dep->nhe);
+ }
+ }
+}
+
+DEFPY (show_interface_nexthop_group,
+ show_interface_nexthop_group_cmd,
+ "show interface [IFNAME$if_name] nexthop-group",
+ SHOW_STR
+ "Interface status and configuration\n"
+ "Interface name\n"
+ "Show Nexthop Groups\n")
+{
+ struct vrf *vrf = NULL;
+ struct interface *ifp = NULL;
+ bool found = false;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ if (if_name) {
+ ifp = if_lookup_by_name(if_name, vrf->vrf_id);
+ if (ifp) {
+ if_nexthop_group_dump_vty(vty, ifp);
+ found = true;
+ }
+ } else {
+ FOR_ALL_INTERFACES (vrf, ifp)
+ if_nexthop_group_dump_vty(vty, ifp);
+ found = true;
+ }
+ }
+
+ if (!found) {
+ vty_out(vty, "%% Can't find interface %s\n", if_name);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_nexthop_group,
+ show_nexthop_group_cmd,
+ "show nexthop-group <(0-4294967295)$id|[<ip$v4|ipv6$v6>] [vrf <NAME$vrf_name|all$vrf_all>]>",
+ SHOW_STR
+ "Show Nexthop Groups\n"
+ "Nexthop Group ID\n"
+ IP_STR
+ IP6_STR
+ VRF_FULL_CMD_HELP_STR)
+{
+
+ struct zebra_vrf *zvrf = NULL;
+ afi_t afi = 0;
+
+ if (id)
+ return show_nexthop_group_id_cmd_helper(vty, id);
+
+ if (v4)
+ afi = AFI_IP;
+ else if (v6)
+ afi = AFI_IP6;
+
+ if (vrf_all) {
+ struct vrf *vrf;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ struct zebra_vrf *zvrf;
+
+ zvrf = vrf->info;
+ if (!zvrf)
+ continue;
+
+ vty_out(vty, "VRF: %s\n", vrf->name);
+ show_nexthop_group_cmd_helper(vty, zvrf, afi);
+ }
+
+ return CMD_SUCCESS;
+ }
+
+ if (vrf_name)
+ zvrf = zebra_vrf_lookup_by_name(vrf_name);
+ else
+ zvrf = zebra_vrf_lookup_by_name(VRF_DEFAULT_NAME);
+
+ if (!zvrf) {
+ vty_out(vty, "VRF %s specified does not exist", vrf_name);
+ return CMD_WARNING;
+ }
+
+ show_nexthop_group_cmd_helper(vty, zvrf, afi);
+
+ return CMD_SUCCESS;
+}
+
DEFUN (no_ip_nht_default_route,
no_ip_nht_default_route_cmd,
"no ip nht resolve-via-default",
@@ -1265,7 +1559,7 @@ DEFPY (show_route_detail,
|X:X::X:X/M$prefix\
>\
>\
- [json$json]",
+ [json$json] [nexthop-group$ng]",
SHOW_STR
IP_STR
"IPv6 forwarding table\n"
@@ -1279,7 +1573,8 @@ DEFPY (show_route_detail,
VRF_FULL_CMD_HELP_STR
"IPv6 Address\n"
"IPv6 prefix\n"
- JSON_STR)
+ JSON_STR
+ "Nexthop Group Information\n")
{
afi_t afi = ipv4 ? AFI_IP : AFI_IP6;
struct route_table *table;
@@ -1288,6 +1583,7 @@ DEFPY (show_route_detail,
bool use_fib = !!fib;
rib_dest_t *dest;
bool network_found = false;
+ bool show_ng = !!ng;
if (address_str)
prefix_str = address_str;
@@ -1321,10 +1617,10 @@ DEFPY (show_route_detail,
network_found = true;
if (json)
- vty_show_ip_route_detail_json(vty, rn,
- use_fib);
+ vty_show_ip_route_detail_json(vty, rn, use_fib);
else
- vty_show_ip_route_detail(vty, rn, 0, use_fib);
+ vty_show_ip_route_detail(vty, rn, 0, use_fib,
+ show_ng);
route_unlock_node(rn);
}
@@ -1376,7 +1672,7 @@ DEFPY (show_route_detail,
if (json)
vty_show_ip_route_detail_json(vty, rn, use_fib);
else
- vty_show_ip_route_detail(vty, rn, 0, use_fib);
+ vty_show_ip_route_detail(vty, rn, 0, use_fib, show_ng);
route_unlock_node(rn);
}
@@ -1539,7 +1835,7 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty,
fib_cnt[ZEBRA_ROUTE_TOTAL]++;
fib_cnt[re->type]++;
}
- for (nexthop = re->ng.nexthop; (!cnt && nexthop);
+ for (nexthop = re->ng->nexthop; (!cnt && nexthop);
nexthop = nexthop->next) {
cnt++;
rib_cnt[ZEBRA_ROUTE_TOTAL]++;
@@ -3033,6 +3329,9 @@ void zebra_vty_init(void)
install_element(CONFIG_NODE, &zebra_packet_process_cmd);
install_element(CONFIG_NODE, &no_zebra_packet_process_cmd);
+ install_element(VIEW_NODE, &show_nexthop_group_cmd);
+ install_element(VIEW_NODE, &show_interface_nexthop_group_cmd);
+
install_element(VIEW_NODE, &show_vrf_cmd);
install_element(VIEW_NODE, &show_vrf_vni_cmd);
install_element(VIEW_NODE, &show_route_cmd);
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index 2417b505ad..1d2748c8ef 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -5775,12 +5775,14 @@ static void process_remote_macip_del(vni_t vni,
vlan_if = zvni_map_to_svi(vxl->access_vlan,
zif->brslave_info.br_if);
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("%s: IP %s (flags 0x%x intf %s) is remote and duplicate, read kernel for local entry",
- __PRETTY_FUNCTION__,
- ipaddr2str(ipaddr, buf1,
- sizeof(buf1)), n->flags,
- vlan_if->name);
- neigh_read_specific_ip(ipaddr, vlan_if);
+ zlog_debug(
+ "%s: IP %s (flags 0x%x intf %s) is remote and duplicate, read kernel for local entry",
+ __PRETTY_FUNCTION__,
+ ipaddr2str(ipaddr, buf1, sizeof(buf1)),
+ n->flags,
+ vlan_if ? vlan_if->name : "Unknown");
+ if (vlan_if)
+ neigh_read_specific_ip(ipaddr, vlan_if);
}
/* When the MAC changes for an IP, it is possible the