summaryrefslogtreecommitdiffstats
path: root/ripd
diff options
context:
space:
mode:
Diffstat (limited to 'ripd')
-rw-r--r--ripd/.gitignore2
-rw-r--r--ripd/Makefile10
-rw-r--r--ripd/rip_bfd.c121
-rw-r--r--ripd/rip_bfd.h23
-rw-r--r--ripd/rip_cli.c1222
-rw-r--r--ripd/rip_debug.c224
-rw-r--r--ripd/rip_debug.h36
-rw-r--r--ripd/rip_errors.c25
-rw-r--r--ripd/rip_errors.h20
-rw-r--r--ripd/rip_interface.c1123
-rw-r--r--ripd/rip_interface.h26
-rw-r--r--ripd/rip_main.c177
-rw-r--r--ripd/rip_nb.c469
-rw-r--r--ripd/rip_nb.h238
-rw-r--r--ripd/rip_nb_config.c1246
-rw-r--r--ripd/rip_nb_notifications.c60
-rw-r--r--ripd/rip_nb_rpcs.c92
-rw-r--r--ripd/rip_nb_state.c433
-rw-r--r--ripd/rip_offset.c146
-rw-r--r--ripd/rip_peer.c209
-rw-r--r--ripd/rip_routemap.c581
-rw-r--r--ripd/rip_snmp.c577
-rw-r--r--ripd/rip_zebra.c248
-rw-r--r--ripd/ripd.c3695
-rw-r--r--ripd/ripd.h544
-rw-r--r--ripd/subdir.am57
26 files changed, 11604 insertions, 0 deletions
diff --git a/ripd/.gitignore b/ripd/.gitignore
new file mode 100644
index 0000000..f149501
--- /dev/null
+++ b/ripd/.gitignore
@@ -0,0 +1,2 @@
+ripd
+ripd.conf
diff --git a/ripd/Makefile b/ripd/Makefile
new file mode 100644
index 0000000..2d6f838
--- /dev/null
+++ b/ripd/Makefile
@@ -0,0 +1,10 @@
+all: ALWAYS
+ @$(MAKE) -s -C .. ripd/ripd
+%: ALWAYS
+ @$(MAKE) -s -C .. ripd/$@
+
+Makefile:
+ #nothing
+ALWAYS:
+.PHONY: ALWAYS makefiles
+.SUFFIXES:
diff --git a/ripd/rip_bfd.c b/ripd/rip_bfd.c
new file mode 100644
index 0000000..b59db11
--- /dev/null
+++ b/ripd/rip_bfd.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RIP BFD integration.
+ * Copyright (C) 2021-2023 Network Device Education Foundation, Inc. ("NetDEF")
+ */
+
+#include <zebra.h>
+
+#include "lib/zclient.h"
+#include "lib/bfd.h"
+
+#include "ripd/ripd.h"
+#include "ripd/rip_bfd.h"
+#include "ripd/rip_debug.h"
+
+DEFINE_MTYPE(RIPD, RIP_BFD_PROFILE, "RIP BFD profile name");
+
+extern struct zclient *zclient;
+
+static const char *rip_bfd_interface_profile(struct rip_interface *ri)
+{
+ struct rip *rip = ri->rip;
+
+ if (ri->bfd.profile)
+ return ri->bfd.profile;
+
+ if (rip->default_bfd_profile)
+ return rip->default_bfd_profile;
+
+ return NULL;
+}
+
+static void rip_bfd_session_change(struct bfd_session_params *bsp,
+ const struct bfd_session_status *bss,
+ void *arg)
+{
+ struct rip_peer *rp = arg;
+
+ /* BFD peer went down. */
+ if (bss->state == BFD_STATUS_DOWN &&
+ bss->previous_state == BFD_STATUS_UP) {
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("%s: peer %pI4: BFD Down", __func__,
+ &rp->addr);
+
+ rip_peer_delete_routes(rp);
+ listnode_delete(rp->rip->peer_list, rp);
+ rip_peer_free(rp);
+ return;
+ }
+
+ /* BFD peer went up. */
+ if (bss->state == BSS_UP && bss->previous_state == BSS_DOWN)
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("%s: peer %pI4: BFD Up", __func__,
+ &rp->addr);
+}
+
+void rip_bfd_session_update(struct rip_peer *rp)
+{
+ struct rip_interface *ri = rp->ri;
+
+ /* BFD configuration was removed. */
+ if (ri == NULL || !ri->bfd.enabled) {
+ bfd_sess_free(&rp->bfd_session);
+ return;
+ }
+
+ /* New BFD session. */
+ if (rp->bfd_session == NULL) {
+ rp->bfd_session = bfd_sess_new(rip_bfd_session_change, rp);
+ bfd_sess_set_ipv4_addrs(rp->bfd_session, NULL, &rp->addr);
+ bfd_sess_set_interface(rp->bfd_session, ri->ifp->name);
+ bfd_sess_set_vrf(rp->bfd_session, rp->rip->vrf->vrf_id);
+ }
+
+ /* Set new configuration. */
+ bfd_sess_set_timers(rp->bfd_session, BFD_DEF_DETECT_MULT,
+ BFD_DEF_MIN_RX, BFD_DEF_MIN_TX);
+ bfd_sess_set_profile(rp->bfd_session, rip_bfd_interface_profile(ri));
+
+ bfd_sess_install(rp->bfd_session);
+}
+
+void rip_bfd_interface_update(struct rip_interface *ri)
+{
+ struct rip *rip;
+ struct rip_peer *rp;
+ struct listnode *node;
+
+ rip = ri->rip;
+ if (!rip)
+ return;
+
+ for (ALL_LIST_ELEMENTS_RO(rip->peer_list, node, rp)) {
+ if (rp->ri != ri)
+ continue;
+
+ rip_bfd_session_update(rp);
+ }
+}
+
+void rip_bfd_instance_update(struct rip *rip)
+{
+ struct interface *ifp;
+
+ FOR_ALL_INTERFACES (rip->vrf, ifp) {
+ struct rip_interface *ri;
+
+ ri = ifp->info;
+ if (!ri)
+ continue;
+
+ rip_bfd_interface_update(ri);
+ }
+}
+
+void rip_bfd_init(struct event_loop *tm)
+{
+ bfd_protocol_integration_init(zclient, tm);
+}
diff --git a/ripd/rip_bfd.h b/ripd/rip_bfd.h
new file mode 100644
index 0000000..7621498
--- /dev/null
+++ b/ripd/rip_bfd.h
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RIP BFD integration.
+ * Copyright (C) 2021-2023 Network Device Education Foundation, Inc. ("NetDEF")
+ */
+
+#ifndef _RIP_BFD_
+#define _RIP_BFD_
+
+#include "frrevent.h"
+
+DECLARE_MTYPE(RIP_BFD_PROFILE);
+
+struct rip;
+struct rip_interface;
+struct rip_peer;
+
+void rip_bfd_session_update(struct rip_peer *rp);
+void rip_bfd_interface_update(struct rip_interface *ri);
+void rip_bfd_instance_update(struct rip *rip);
+void rip_bfd_init(struct event_loop *tm);
+
+#endif /* _RIP_BFD_ */
diff --git a/ripd/rip_cli.c b/ripd/rip_cli.c
new file mode 100644
index 0000000..097c708
--- /dev/null
+++ b/ripd/rip_cli.c
@@ -0,0 +1,1222 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "vrf.h"
+#include "log.h"
+#include "prefix.h"
+#include "command.h"
+#include "northbound_cli.h"
+#include "libfrr.h"
+
+#include "ripd/ripd.h"
+#include "ripd/rip_nb.h"
+#include "ripd/rip_cli_clippy.c"
+
+/*
+ * XPath: /frr-ripd:ripd/instance
+ */
+DEFPY_YANG_NOSH (router_rip,
+ router_rip_cmd,
+ "router rip [vrf NAME]",
+ "Enable a routing process\n"
+ "Routing Information Protocol (RIP)\n"
+ VRF_CMD_HELP_STR)
+{
+ char xpath[XPATH_MAXLEN];
+ int ret;
+
+ /* Build RIP instance XPath. */
+ if (!vrf)
+ vrf = VRF_DEFAULT_NAME;
+ snprintf(xpath, sizeof(xpath), "/frr-ripd:ripd/instance[vrf='%s']",
+ vrf);
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ ret = nb_cli_apply_changes(vty, NULL);
+ if (ret == CMD_SUCCESS)
+ VTY_PUSH_XPATH(RIP_NODE, xpath);
+
+ return ret;
+}
+
+DEFPY_YANG (no_router_rip,
+ no_router_rip_cmd,
+ "no router rip [vrf NAME]",
+ NO_STR
+ "Enable a routing process\n"
+ "Routing Information Protocol (RIP)\n"
+ VRF_CMD_HELP_STR)
+{
+ char xpath[XPATH_MAXLEN];
+
+ /* Build RIP instance XPath. */
+ if (!vrf)
+ vrf = VRF_DEFAULT_NAME;
+ snprintf(xpath, sizeof(xpath), "/frr-ripd:ripd/instance[vrf='%s']",
+ vrf);
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes_clear_pending(vty, NULL);
+}
+
+void cli_show_router_rip(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *vrf_name;
+
+ vrf_name = yang_dnode_get_string(dnode, "./vrf");
+
+ vty_out(vty, "!\n");
+ vty_out(vty, "router rip");
+ if (!strmatch(vrf_name, VRF_DEFAULT_NAME))
+ vty_out(vty, " vrf %s", vrf_name);
+ vty_out(vty, "\n");
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/allow-ecmp
+ */
+DEFUN_YANG (rip_allow_ecmp,
+ rip_allow_ecmp_cmd,
+ "allow-ecmp [" CMD_RANGE_STR(1, MULTIPATH_NUM) "]",
+ "Allow Equal Cost MultiPath\n"
+ "Number of paths\n")
+{
+ int idx_number = 0;
+ char mpaths[3] = {};
+ uint32_t paths = MULTIPATH_NUM;
+
+ if (argv_find(argv, argc, CMD_RANGE_STR(1, MULTIPATH_NUM), &idx_number))
+ paths = strtol(argv[idx_number]->arg, NULL, 10);
+ snprintf(mpaths, sizeof(mpaths), "%u", paths);
+
+ nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY, mpaths);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_rip_allow_ecmp,
+ no_rip_allow_ecmp_cmd,
+ "no allow-ecmp [" CMD_RANGE_STR(1, MULTIPATH_NUM) "]",
+ NO_STR
+ "Allow Equal Cost MultiPath\n"
+ "Number of paths\n")
+{
+ nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY, 0);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_rip_allow_ecmp(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ uint8_t paths;
+
+ paths = yang_dnode_get_uint8(dnode, NULL);
+
+ if (!paths)
+ vty_out(vty, " no allow-ecmp\n");
+ else
+ vty_out(vty, " allow-ecmp %d\n", paths);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/default-information-originate
+ */
+DEFPY_YANG (rip_default_information_originate,
+ rip_default_information_originate_cmd,
+ "[no] default-information originate",
+ NO_STR
+ "Control distribution of default route\n"
+ "Distribute a default route\n")
+{
+ nb_cli_enqueue_change(vty, "./default-information-originate",
+ NB_OP_MODIFY, no ? "false" : "true");
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_rip_default_information_originate(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ if (!yang_dnode_get_bool(dnode, NULL))
+ vty_out(vty, " no");
+
+ vty_out(vty, " default-information originate\n");
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/default-metric
+ */
+DEFPY_YANG (rip_default_metric,
+ rip_default_metric_cmd,
+ "default-metric (1-16)",
+ "Set a metric of redistribute routes\n"
+ "Default metric\n")
+{
+ nb_cli_enqueue_change(vty, "./default-metric", NB_OP_MODIFY,
+ default_metric_str);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG (no_rip_default_metric,
+ no_rip_default_metric_cmd,
+ "no default-metric [(1-16)]",
+ NO_STR
+ "Set a metric of redistribute routes\n"
+ "Default metric\n")
+{
+ nb_cli_enqueue_change(vty, "./default-metric", NB_OP_MODIFY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_rip_default_metric(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " default-metric %s\n",
+ yang_dnode_get_string(dnode, NULL));
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/distance/default
+ */
+DEFPY_YANG (rip_distance,
+ rip_distance_cmd,
+ "distance (1-255)",
+ "Administrative distance\n"
+ "Distance value\n")
+{
+ nb_cli_enqueue_change(vty, "./distance/default", NB_OP_MODIFY,
+ distance_str);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG (no_rip_distance,
+ no_rip_distance_cmd,
+ "no distance [(1-255)]",
+ NO_STR
+ "Administrative distance\n"
+ "Distance value\n")
+{
+ nb_cli_enqueue_change(vty, "./distance/default", NB_OP_MODIFY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_rip_distance(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ if (yang_dnode_is_default(dnode, NULL))
+ vty_out(vty, " no distance\n");
+ else
+ vty_out(vty, " distance %s\n",
+ yang_dnode_get_string(dnode, NULL));
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/distance/source
+ */
+DEFPY_YANG (rip_distance_source,
+ rip_distance_source_cmd,
+ "[no] distance (1-255) A.B.C.D/M$prefix [WORD$acl]",
+ NO_STR
+ "Administrative distance\n"
+ "Distance value\n"
+ "IP source prefix\n"
+ "Access list name\n")
+{
+ if (!no) {
+ nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL);
+ nb_cli_enqueue_change(vty, "./distance", NB_OP_MODIFY,
+ distance_str);
+ nb_cli_enqueue_change(vty, "./access-list",
+ acl ? NB_OP_MODIFY : NB_OP_DESTROY, acl);
+ } else
+ nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, "./distance/source[prefix='%s']",
+ prefix_str);
+}
+
+void cli_show_rip_distance_source(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " distance %s %s",
+ yang_dnode_get_string(dnode, "./distance"),
+ yang_dnode_get_string(dnode, "./prefix"));
+ if (yang_dnode_exists(dnode, "./access-list"))
+ vty_out(vty, " %s",
+ yang_dnode_get_string(dnode, "./access-list"));
+ vty_out(vty, "\n");
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/explicit-neighbor
+ */
+DEFPY_YANG (rip_neighbor,
+ rip_neighbor_cmd,
+ "[no] neighbor A.B.C.D",
+ NO_STR
+ "Specify a neighbor router\n"
+ "Neighbor address\n")
+{
+ nb_cli_enqueue_change(vty, "./explicit-neighbor",
+ no ? NB_OP_DESTROY : NB_OP_CREATE, neighbor_str);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_rip_neighbor(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " neighbor %s\n", yang_dnode_get_string(dnode, NULL));
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/network
+ */
+DEFPY_YANG (rip_network_prefix,
+ rip_network_prefix_cmd,
+ "[no] network A.B.C.D/M",
+ NO_STR
+ "Enable routing on an IP network\n"
+ "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+ nb_cli_enqueue_change(vty, "./network",
+ no ? NB_OP_DESTROY : NB_OP_CREATE, network_str);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_rip_network_prefix(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " network %s\n", yang_dnode_get_string(dnode, NULL));
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/interface
+ */
+DEFPY_YANG (rip_network_if,
+ rip_network_if_cmd,
+ "[no] network WORD",
+ NO_STR
+ "Enable routing on an IP network\n"
+ "Interface name\n")
+{
+ nb_cli_enqueue_change(vty, "./interface",
+ no ? NB_OP_DESTROY : NB_OP_CREATE, network);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_rip_network_interface(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " network %s\n", yang_dnode_get_string(dnode, NULL));
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/offset-list
+ */
+DEFPY_YANG (rip_offset_list,
+ rip_offset_list_cmd,
+ "[no] offset-list ACCESSLIST4_NAME$acl <in|out>$direction (0-16)$metric [IFNAME]",
+ NO_STR
+ "Modify RIP metric\n"
+ "Access-list name\n"
+ "For incoming updates\n"
+ "For outgoing updates\n"
+ "Metric value\n"
+ "Interface to match\n")
+{
+ if (!no) {
+ nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL);
+ nb_cli_enqueue_change(vty, "./access-list", NB_OP_MODIFY, acl);
+ nb_cli_enqueue_change(vty, "./metric", NB_OP_MODIFY,
+ metric_str);
+ } else
+ nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(
+ vty, "./offset-list[interface='%s'][direction='%s']",
+ ifname ? ifname : "*", direction);
+}
+
+void cli_show_rip_offset_list(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *interface;
+
+ interface = yang_dnode_get_string(dnode, "./interface");
+
+ vty_out(vty, " offset-list %s %s %s",
+ yang_dnode_get_string(dnode, "./access-list"),
+ yang_dnode_get_string(dnode, "./direction"),
+ yang_dnode_get_string(dnode, "./metric"));
+ if (!strmatch(interface, "*"))
+ vty_out(vty, " %s", interface);
+ vty_out(vty, "\n");
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/passive-default
+ */
+DEFPY_YANG (rip_passive_default,
+ rip_passive_default_cmd,
+ "[no] passive-interface default",
+ NO_STR
+ "Suppress routing updates on an interface\n"
+ "default for all interfaces\n")
+{
+ nb_cli_enqueue_change(vty, "./passive-default", NB_OP_MODIFY,
+ no ? "false" : "true");
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_rip_passive_default(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ if (!yang_dnode_get_bool(dnode, NULL))
+ vty_out(vty, " no");
+
+ vty_out(vty, " passive-interface default\n");
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/passive-interface
+ * /frr-ripd:ripd/instance/non-passive-interface
+ */
+DEFPY_YANG (rip_passive_interface,
+ rip_passive_interface_cmd,
+ "[no] passive-interface IFNAME",
+ NO_STR
+ "Suppress routing updates on an interface\n"
+ "Interface name\n")
+{
+ bool passive_default =
+ yang_dnode_get_bool(vty->candidate_config->dnode, "%s%s",
+ VTY_CURR_XPATH, "/passive-default");
+
+ if (passive_default) {
+ nb_cli_enqueue_change(vty, "./non-passive-interface",
+ no ? NB_OP_CREATE : NB_OP_DESTROY,
+ ifname);
+ } else {
+ nb_cli_enqueue_change(vty, "./passive-interface",
+ no ? NB_OP_DESTROY : NB_OP_CREATE,
+ ifname);
+ }
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_rip_passive_interface(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " passive-interface %s\n",
+ yang_dnode_get_string(dnode, NULL));
+}
+
+void cli_show_rip_non_passive_interface(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " no passive-interface %s\n",
+ yang_dnode_get_string(dnode, NULL));
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/redistribute
+ */
+DEFPY_YANG (rip_redistribute,
+ rip_redistribute_cmd,
+ "[no] redistribute " FRR_REDIST_STR_RIPD "$protocol [{metric (0-16)|route-map RMAP_NAME$route_map}]",
+ NO_STR
+ REDIST_STR
+ FRR_REDIST_HELP_STR_RIPD
+ "Metric\n"
+ "Metric value\n"
+ "Route map reference\n"
+ "Pointer to route-map entries\n")
+{
+ if (!no) {
+ nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL);
+ nb_cli_enqueue_change(vty, "./route-map",
+ route_map ? NB_OP_MODIFY : NB_OP_DESTROY,
+ route_map);
+ nb_cli_enqueue_change(vty, "./metric",
+ metric_str ? NB_OP_MODIFY : NB_OP_DESTROY,
+ metric_str);
+ } else
+ nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, "./redistribute[protocol='%s']",
+ protocol);
+}
+
+void cli_show_rip_redistribute(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " redistribute %s",
+ yang_dnode_get_string(dnode, "./protocol"));
+ if (yang_dnode_exists(dnode, "./metric"))
+ vty_out(vty, " metric %s",
+ yang_dnode_get_string(dnode, "./metric"));
+ if (yang_dnode_exists(dnode, "./route-map"))
+ vty_out(vty, " route-map %s",
+ yang_dnode_get_string(dnode, "./route-map"));
+ vty_out(vty, "\n");
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/static-route
+ */
+DEFPY_YANG (rip_route,
+ rip_route_cmd,
+ "[no] route A.B.C.D/M",
+ NO_STR
+ "RIP static route configuration\n"
+ "IP prefix <network>/<length>\n")
+{
+ nb_cli_enqueue_change(vty, "./static-route",
+ no ? NB_OP_DESTROY : NB_OP_CREATE, route_str);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_rip_route(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " route %s\n", yang_dnode_get_string(dnode, NULL));
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/timers
+ */
+DEFPY_YANG (rip_timers,
+ rip_timers_cmd,
+ "timers basic (5-2147483647)$update (5-2147483647)$timeout (5-2147483647)$garbage",
+ "Adjust routing timers\n"
+ "Basic routing protocol update timers\n"
+ "Routing table update timer value in second. Default is 30.\n"
+ "Routing information timeout timer. Default is 180.\n"
+ "Garbage collection timer. Default is 120.\n")
+{
+ nb_cli_enqueue_change(vty, "./update-interval", NB_OP_MODIFY,
+ update_str);
+ nb_cli_enqueue_change(vty, "./holddown-interval", NB_OP_MODIFY,
+ timeout_str);
+ nb_cli_enqueue_change(vty, "./flush-interval", NB_OP_MODIFY,
+ garbage_str);
+
+ return nb_cli_apply_changes(vty, "./timers");
+}
+
+DEFPY_YANG (no_rip_timers,
+ no_rip_timers_cmd,
+ "no timers basic [(5-2147483647) (5-2147483647) (5-2147483647)]",
+ NO_STR
+ "Adjust routing timers\n"
+ "Basic routing protocol update timers\n"
+ "Routing table update timer value in second. Default is 30.\n"
+ "Routing information timeout timer. Default is 180.\n"
+ "Garbage collection timer. Default is 120.\n")
+{
+ nb_cli_enqueue_change(vty, "./update-interval", NB_OP_MODIFY, NULL);
+ nb_cli_enqueue_change(vty, "./holddown-interval", NB_OP_MODIFY, NULL);
+ nb_cli_enqueue_change(vty, "./flush-interval", NB_OP_MODIFY, NULL);
+
+ return nb_cli_apply_changes(vty, "./timers");
+}
+
+void cli_show_rip_timers(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " timers basic %s %s %s\n",
+ yang_dnode_get_string(dnode, "./update-interval"),
+ yang_dnode_get_string(dnode, "./holddown-interval"),
+ yang_dnode_get_string(dnode, "./flush-interval"));
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/version
+ */
+DEFPY_YANG (rip_version,
+ rip_version_cmd,
+ "version (1-2)",
+ "Set routing protocol version\n"
+ "version\n")
+{
+ nb_cli_enqueue_change(vty, "./version/receive", NB_OP_MODIFY,
+ version_str);
+ nb_cli_enqueue_change(vty, "./version/send", NB_OP_MODIFY, version_str);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG (no_rip_version,
+ no_rip_version_cmd,
+ "no version [(1-2)]",
+ NO_STR
+ "Set routing protocol version\n"
+ "version\n")
+{
+ nb_cli_enqueue_change(vty, "./version/receive", NB_OP_MODIFY, NULL);
+ nb_cli_enqueue_change(vty, "./version/send", NB_OP_MODIFY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_rip_version(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ /*
+ * We have only one "version" command and three possible combinations of
+ * send/receive values.
+ */
+ switch (yang_dnode_get_enum(dnode, "./receive")) {
+ case RI_RIP_VERSION_1:
+ vty_out(vty, " version 1\n");
+ break;
+ case RI_RIP_VERSION_2:
+ vty_out(vty, " version 2\n");
+ break;
+ case RI_RIP_VERSION_1_AND_2:
+ vty_out(vty, " no version\n");
+ break;
+ }
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/default-bfd-profile
+ */
+DEFPY_YANG(rip_bfd_default_profile, rip_bfd_default_profile_cmd,
+ "bfd default-profile BFDPROF$profile",
+ "Bidirectional Forwarding Detection\n"
+ "BFD default profile\n"
+ "Profile name\n")
+{
+ nb_cli_enqueue_change(vty, "./default-bfd-profile", NB_OP_MODIFY,
+ profile);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(no_rip_bfd_default_profile, no_rip_bfd_default_profile_cmd,
+ "no bfd default-profile [BFDPROF]",
+ NO_STR
+ "Bidirectional Forwarding Detection\n"
+ "BFD default profile\n"
+ "Profile name\n")
+{
+ nb_cli_enqueue_change(vty, "./default-bfd-profile", NB_OP_DESTROY,
+ NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_ripd_instance_default_bfd_profile(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " bfd default-profile %s\n",
+ yang_dnode_get_string(dnode, NULL));
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/split-horizon
+ */
+DEFPY_YANG (ip_rip_split_horizon,
+ ip_rip_split_horizon_cmd,
+ "[no] ip rip split-horizon [poisoned-reverse$poisoned_reverse]",
+ NO_STR
+ IP_STR
+ "Routing Information Protocol\n"
+ "Perform split horizon\n"
+ "With poisoned-reverse\n")
+{
+ const char *value;
+
+ if (no)
+ value = "disabled";
+ else if (poisoned_reverse)
+ value = "poison-reverse";
+ else
+ value = "simple";
+
+ nb_cli_enqueue_change(vty, "./split-horizon", NB_OP_MODIFY, value);
+
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+void cli_show_ip_rip_split_horizon(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ int value;
+
+ value = yang_dnode_get_enum(dnode, NULL);
+ switch (value) {
+ case RIP_NO_SPLIT_HORIZON:
+ vty_out(vty, " no ip rip split-horizon\n");
+ break;
+ case RIP_SPLIT_HORIZON:
+ vty_out(vty, " ip rip split-horizon\n");
+ break;
+ case RIP_SPLIT_HORIZON_POISONED_REVERSE:
+ vty_out(vty, " ip rip split-horizon poisoned-reverse\n");
+ break;
+ }
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/v2-broadcast
+ */
+DEFPY_YANG (ip_rip_v2_broadcast,
+ ip_rip_v2_broadcast_cmd,
+ "[no] ip rip v2-broadcast",
+ NO_STR
+ IP_STR
+ "Routing Information Protocol\n"
+ "Send ip broadcast v2 update\n")
+{
+ nb_cli_enqueue_change(vty, "./v2-broadcast", NB_OP_MODIFY,
+ no ? "false" : "true");
+
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+void cli_show_ip_rip_v2_broadcast(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ if (!yang_dnode_get_bool(dnode, NULL))
+ vty_out(vty, " no");
+
+ vty_out(vty, " ip rip v2-broadcast\n");
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/version-receive
+ */
+DEFPY_YANG (ip_rip_receive_version,
+ ip_rip_receive_version_cmd,
+ "ip rip receive version <{1$v1|2$v2}|none>",
+ IP_STR
+ "Routing Information Protocol\n"
+ "Advertisement reception\n"
+ "Version control\n"
+ "RIP version 1\n"
+ "RIP version 2\n"
+ "None\n")
+{
+ const char *value;
+
+ if (v1 && v2)
+ value = "both";
+ else if (v1)
+ value = "1";
+ else if (v2)
+ value = "2";
+ else
+ value = "none";
+
+ nb_cli_enqueue_change(vty, "./version-receive", NB_OP_MODIFY, value);
+
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+DEFPY_YANG (no_ip_rip_receive_version,
+ no_ip_rip_receive_version_cmd,
+ "no ip rip receive version [<{1|2}|none>]",
+ NO_STR
+ IP_STR
+ "Routing Information Protocol\n"
+ "Advertisement reception\n"
+ "Version control\n"
+ "RIP version 1\n"
+ "RIP version 2\n"
+ "None\n")
+{
+ nb_cli_enqueue_change(vty, "./version-receive", NB_OP_MODIFY, NULL);
+
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+void cli_show_ip_rip_receive_version(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ switch (yang_dnode_get_enum(dnode, NULL)) {
+ case RI_RIP_UNSPEC:
+ vty_out(vty, " no ip rip receive version\n");
+ break;
+ case RI_RIP_VERSION_1:
+ vty_out(vty, " ip rip receive version 1\n");
+ break;
+ case RI_RIP_VERSION_2:
+ vty_out(vty, " ip rip receive version 2\n");
+ break;
+ case RI_RIP_VERSION_1_AND_2:
+ vty_out(vty, " ip rip receive version 1 2\n");
+ break;
+ case RI_RIP_VERSION_NONE:
+ vty_out(vty, " ip rip receive version none\n");
+ break;
+ }
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/version-send
+ */
+DEFPY_YANG (ip_rip_send_version,
+ ip_rip_send_version_cmd,
+ "ip rip send version <{1$v1|2$v2}|none>",
+ IP_STR
+ "Routing Information Protocol\n"
+ "Advertisement transmission\n"
+ "Version control\n"
+ "RIP version 1\n"
+ "RIP version 2\n"
+ "None\n")
+{
+ const char *value;
+
+ if (v1 && v2)
+ value = "both";
+ else if (v1)
+ value = "1";
+ else if (v2)
+ value = "2";
+ else
+ value = "none";
+
+ nb_cli_enqueue_change(vty, "./version-send", NB_OP_MODIFY, value);
+
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+DEFPY_YANG (no_ip_rip_send_version,
+ no_ip_rip_send_version_cmd,
+ "no ip rip send version [<{1|2}|none>]",
+ NO_STR
+ IP_STR
+ "Routing Information Protocol\n"
+ "Advertisement transmission\n"
+ "Version control\n"
+ "RIP version 1\n"
+ "RIP version 2\n"
+ "None\n")
+{
+ nb_cli_enqueue_change(vty, "./version-send", NB_OP_MODIFY, NULL);
+
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+void cli_show_ip_rip_send_version(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ switch (yang_dnode_get_enum(dnode, NULL)) {
+ case RI_RIP_UNSPEC:
+ vty_out(vty, " no ip rip send version\n");
+ break;
+ case RI_RIP_VERSION_1:
+ vty_out(vty, " ip rip send version 1\n");
+ break;
+ case RI_RIP_VERSION_2:
+ vty_out(vty, " ip rip send version 2\n");
+ break;
+ case RI_RIP_VERSION_1_AND_2:
+ vty_out(vty, " ip rip send version 1 2\n");
+ break;
+ case RI_RIP_VERSION_NONE:
+ vty_out(vty, " ip rip send version none\n");
+ break;
+ }
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-scheme
+ */
+DEFPY_YANG (ip_rip_authentication_mode,
+ ip_rip_authentication_mode_cmd,
+ "ip rip authentication mode <md5$mode [auth-length <rfc|old-ripd>$auth_length]|text$mode>",
+ IP_STR
+ "Routing Information Protocol\n"
+ "Authentication control\n"
+ "Authentication mode\n"
+ "Keyed message digest\n"
+ "MD5 authentication data length\n"
+ "RFC compatible\n"
+ "Old ripd compatible\n"
+ "Clear text authentication\n")
+{
+ const char *value = NULL;
+
+ if (auth_length) {
+ if (strmatch(auth_length, "rfc"))
+ value = "16";
+ else
+ value = "20";
+ }
+
+ nb_cli_enqueue_change(vty, "./authentication-scheme/mode", NB_OP_MODIFY,
+ strmatch(mode, "md5") ? "md5" : "plain-text");
+ if (strmatch(mode, "md5"))
+ nb_cli_enqueue_change(vty,
+ "./authentication-scheme/md5-auth-length",
+ NB_OP_MODIFY, value);
+
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+DEFPY_YANG (no_ip_rip_authentication_mode,
+ no_ip_rip_authentication_mode_cmd,
+ "no ip rip authentication mode [<md5 [auth-length <rfc|old-ripd>]|text>]",
+ NO_STR
+ IP_STR
+ "Routing Information Protocol\n"
+ "Authentication control\n"
+ "Authentication mode\n"
+ "Keyed message digest\n"
+ "MD5 authentication data length\n"
+ "RFC compatible\n"
+ "Old ripd compatible\n"
+ "Clear text authentication\n")
+{
+ nb_cli_enqueue_change(vty, "./authentication-scheme/mode", NB_OP_MODIFY,
+ NULL);
+ nb_cli_enqueue_change(vty, "./authentication-scheme/md5-auth-length",
+ NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+void cli_show_ip_rip_authentication_scheme(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ switch (yang_dnode_get_enum(dnode, "./mode")) {
+ case RIP_NO_AUTH:
+ vty_out(vty, " no ip rip authentication mode\n");
+ break;
+ case RIP_AUTH_SIMPLE_PASSWORD:
+ vty_out(vty, " ip rip authentication mode text\n");
+ break;
+ case RIP_AUTH_MD5:
+ vty_out(vty, " ip rip authentication mode md5");
+ if (show_defaults
+ || !yang_dnode_is_default(dnode, "./md5-auth-length")) {
+ if (yang_dnode_get_enum(dnode, "./md5-auth-length")
+ == RIP_AUTH_MD5_SIZE)
+ vty_out(vty, " auth-length rfc");
+ else
+ vty_out(vty, " auth-length old-ripd");
+ }
+ vty_out(vty, "\n");
+ break;
+ }
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-password
+ */
+DEFPY_YANG (ip_rip_authentication_string,
+ ip_rip_authentication_string_cmd,
+ "ip rip authentication string LINE$password",
+ IP_STR
+ "Routing Information Protocol\n"
+ "Authentication control\n"
+ "Authentication string\n"
+ "Authentication string\n")
+{
+ if (strlen(password) > 16) {
+ vty_out(vty,
+ "%% RIPv2 authentication string must be shorter than 16\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (yang_dnode_existsf(vty->candidate_config->dnode, "%s%s",
+ VTY_CURR_XPATH,
+ "/frr-ripd:rip/authentication-key-chain")) {
+ vty_out(vty, "%% key-chain configuration exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ nb_cli_enqueue_change(vty, "./authentication-password", NB_OP_MODIFY,
+ password);
+
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+DEFPY_YANG (no_ip_rip_authentication_string,
+ no_ip_rip_authentication_string_cmd,
+ "no ip rip authentication string [LINE]",
+ NO_STR
+ IP_STR
+ "Routing Information Protocol\n"
+ "Authentication control\n"
+ "Authentication string\n"
+ "Authentication string\n")
+{
+ nb_cli_enqueue_change(vty, "./authentication-password", NB_OP_DESTROY,
+ NULL);
+
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+void cli_show_ip_rip_authentication_string(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " ip rip authentication string %s\n",
+ yang_dnode_get_string(dnode, NULL));
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-key-chain
+ */
+DEFPY_YANG (ip_rip_authentication_key_chain,
+ ip_rip_authentication_key_chain_cmd,
+ "ip rip authentication key-chain LINE$keychain",
+ IP_STR
+ "Routing Information Protocol\n"
+ "Authentication control\n"
+ "Authentication key-chain\n"
+ "name of key-chain\n")
+{
+ if (yang_dnode_existsf(vty->candidate_config->dnode, "%s%s",
+ VTY_CURR_XPATH,
+ "/frr-ripd:rip/authentication-password")) {
+ vty_out(vty, "%% authentication string configuration exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ nb_cli_enqueue_change(vty, "./authentication-key-chain", NB_OP_MODIFY,
+ keychain);
+
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+DEFPY_YANG (no_ip_rip_authentication_key_chain,
+ no_ip_rip_authentication_key_chain_cmd,
+ "no ip rip authentication key-chain [LINE]",
+ NO_STR
+ IP_STR
+ "Routing Information Protocol\n"
+ "Authentication control\n"
+ "Authentication key-chain\n"
+ "name of key-chain\n")
+{
+ nb_cli_enqueue_change(vty, "./authentication-key-chain", NB_OP_DESTROY,
+ NULL);
+
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+void cli_show_ip_rip_authentication_key_chain(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " ip rip authentication key-chain %s\n",
+ yang_dnode_get_string(dnode, NULL));
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/enable
+ */
+DEFPY_YANG(ip_rip_bfd, ip_rip_bfd_cmd, "[no] ip rip bfd",
+ NO_STR IP_STR
+ "Routing Information Protocol\n"
+ "Enable BFD support\n")
+{
+ nb_cli_enqueue_change(vty, "./bfd-monitoring/enable", NB_OP_MODIFY,
+ no ? "false" : "true");
+
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+void cli_show_ip_rip_bfd_enable(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " ip rip bfd\n");
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd/profile
+ */
+DEFPY_YANG(ip_rip_bfd_profile, ip_rip_bfd_profile_cmd,
+ "[no] ip rip bfd profile BFDPROF$profile",
+ NO_STR IP_STR
+ "Routing Information Protocol\n"
+ "Enable BFD support\n"
+ "Use a pre-configured profile\n"
+ "Profile name\n")
+{
+ if (no)
+ nb_cli_enqueue_change(vty, "./bfd-monitoring/profile",
+ NB_OP_DESTROY, NULL);
+ else
+ nb_cli_enqueue_change(vty, "./bfd-monitoring/profile",
+ NB_OP_MODIFY, profile);
+
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+DEFPY_YANG(no_ip_rip_bfd_profile, no_ip_rip_bfd_profile_cmd,
+ "no ip rip bfd profile",
+ NO_STR IP_STR
+ "Routing Information Protocol\n"
+ "Enable BFD support\n"
+ "Use a pre-configured profile\n")
+{
+ nb_cli_enqueue_change(vty, "./bfd-monitoring/profile", NB_OP_DESTROY,
+ NULL);
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+void cli_show_ip_rip_bfd_profile(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " ip rip bfd profile %s\n",
+ yang_dnode_get_string(dnode, NULL));
+}
+
+/*
+ * XPath: /frr-ripd:clear-rip-route
+ */
+DEFPY_YANG (clear_ip_rip,
+ clear_ip_rip_cmd,
+ "clear ip rip [vrf WORD]",
+ CLEAR_STR
+ IP_STR
+ "Clear IP RIP database\n"
+ VRF_CMD_HELP_STR)
+{
+ struct list *input;
+ int ret;
+
+ input = list_new();
+ if (vrf) {
+ struct yang_data *yang_vrf;
+
+ yang_vrf = yang_data_new("/frr-ripd:clear-rip-route/input/vrf",
+ vrf);
+ listnode_add(input, yang_vrf);
+ }
+
+ ret = nb_cli_rpc(vty, "/frr-ripd:clear-rip-route", input, NULL);
+
+ list_delete(&input);
+
+ return ret;
+}
+
+DEFUN (rip_distribute_list,
+ rip_distribute_list_cmd,
+ "distribute-list [prefix] ACCESSLIST4_NAME <in|out> [WORD]",
+ "Filter networks in routing updates\n"
+ "Specify a prefix\n"
+ "Access-list name\n"
+ "Filter incoming routing updates\n"
+ "Filter outgoing routing updates\n"
+ "Interface name\n")
+{
+ const char *ifname = NULL;
+ int prefix = (argv[1]->type == WORD_TKN) ? 1 : 0;
+
+ if (argv[argc - 1]->type == VARIABLE_TKN)
+ ifname = argv[argc - 1]->arg;
+
+ return distribute_list_parser(prefix, true, argv[2 + prefix]->text,
+ argv[1 + prefix]->arg, ifname);
+}
+
+DEFUN (rip_no_distribute_list,
+ rip_no_distribute_list_cmd,
+ "no distribute-list [prefix] ACCESSLIST4_NAME <in|out> [WORD]",
+ NO_STR
+ "Filter networks in routing updates\n"
+ "Specify a prefix\n"
+ "Access-list name\n"
+ "Filter incoming routing updates\n"
+ "Filter outgoing routing updates\n"
+ "Interface name\n")
+{
+ const char *ifname = NULL;
+ int prefix = (argv[2]->type == WORD_TKN) ? 1 : 0;
+
+ if (argv[argc - 1]->type == VARIABLE_TKN)
+ ifname = argv[argc - 1]->arg;
+
+ return distribute_list_no_parser(vty, prefix, true,
+ argv[3 + prefix]->text,
+ argv[2 + prefix]->arg, ifname);
+}
+
+void rip_cli_init(void)
+{
+ install_element(CONFIG_NODE, &router_rip_cmd);
+ install_element(CONFIG_NODE, &no_router_rip_cmd);
+
+ install_element(RIP_NODE, &rip_distribute_list_cmd);
+ install_element(RIP_NODE, &rip_no_distribute_list_cmd);
+
+ install_element(RIP_NODE, &rip_allow_ecmp_cmd);
+ install_element(RIP_NODE, &no_rip_allow_ecmp_cmd);
+ install_element(RIP_NODE, &rip_default_information_originate_cmd);
+ install_element(RIP_NODE, &rip_default_metric_cmd);
+ install_element(RIP_NODE, &no_rip_default_metric_cmd);
+ install_element(RIP_NODE, &rip_distance_cmd);
+ install_element(RIP_NODE, &no_rip_distance_cmd);
+ install_element(RIP_NODE, &rip_distance_source_cmd);
+ install_element(RIP_NODE, &rip_neighbor_cmd);
+ install_element(RIP_NODE, &rip_network_prefix_cmd);
+ install_element(RIP_NODE, &rip_network_if_cmd);
+ install_element(RIP_NODE, &rip_offset_list_cmd);
+ install_element(RIP_NODE, &rip_passive_default_cmd);
+ install_element(RIP_NODE, &rip_passive_interface_cmd);
+ install_element(RIP_NODE, &rip_redistribute_cmd);
+ install_element(RIP_NODE, &rip_route_cmd);
+ install_element(RIP_NODE, &rip_timers_cmd);
+ install_element(RIP_NODE, &no_rip_timers_cmd);
+ install_element(RIP_NODE, &rip_version_cmd);
+ install_element(RIP_NODE, &no_rip_version_cmd);
+ install_element(RIP_NODE, &rip_bfd_default_profile_cmd);
+ install_element(RIP_NODE, &no_rip_bfd_default_profile_cmd);
+
+ install_element(INTERFACE_NODE, &ip_rip_split_horizon_cmd);
+ install_element(INTERFACE_NODE, &ip_rip_v2_broadcast_cmd);
+ install_element(INTERFACE_NODE, &ip_rip_receive_version_cmd);
+ install_element(INTERFACE_NODE, &no_ip_rip_receive_version_cmd);
+ install_element(INTERFACE_NODE, &ip_rip_send_version_cmd);
+ install_element(INTERFACE_NODE, &no_ip_rip_send_version_cmd);
+ install_element(INTERFACE_NODE, &ip_rip_authentication_mode_cmd);
+ install_element(INTERFACE_NODE, &no_ip_rip_authentication_mode_cmd);
+ install_element(INTERFACE_NODE, &ip_rip_authentication_string_cmd);
+ install_element(INTERFACE_NODE, &no_ip_rip_authentication_string_cmd);
+ install_element(INTERFACE_NODE, &ip_rip_authentication_key_chain_cmd);
+ install_element(INTERFACE_NODE,
+ &no_ip_rip_authentication_key_chain_cmd);
+ install_element(INTERFACE_NODE, &ip_rip_bfd_cmd);
+ install_element(INTERFACE_NODE, &ip_rip_bfd_profile_cmd);
+ install_element(INTERFACE_NODE, &no_ip_rip_bfd_profile_cmd);
+
+ install_element(ENABLE_NODE, &clear_ip_rip_cmd);
+}
diff --git a/ripd/rip_debug.c b/ripd/rip_debug.c
new file mode 100644
index 0000000..e91d791
--- /dev/null
+++ b/ripd/rip_debug.c
@@ -0,0 +1,224 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RIP debug routines
+ * Copyright (C) 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
+ */
+
+#include <zebra.h>
+#include "command.h"
+#include "ripd/rip_debug.h"
+
+/* For debug statement. */
+unsigned long rip_debug_event = 0;
+unsigned long rip_debug_packet = 0;
+unsigned long rip_debug_zebra = 0;
+
+DEFUN_NOSH (show_debugging_rip,
+ show_debugging_rip_cmd,
+ "show debugging [rip]",
+ SHOW_STR
+ DEBUG_STR
+ RIP_STR)
+{
+ vty_out(vty, "RIP debugging status:\n");
+
+ if (IS_RIP_DEBUG_EVENT)
+ vty_out(vty, " RIP event debugging is on\n");
+
+ if (IS_RIP_DEBUG_PACKET) {
+ if (IS_RIP_DEBUG_SEND && IS_RIP_DEBUG_RECV) {
+ vty_out(vty, " RIP packet debugging is on\n");
+ } else {
+ if (IS_RIP_DEBUG_SEND)
+ vty_out(vty,
+ " RIP packet send debugging is on\n");
+ else
+ vty_out(vty,
+ " RIP packet receive debugging is on\n");
+ }
+ }
+
+ if (IS_RIP_DEBUG_ZEBRA)
+ vty_out(vty, " RIP zebra debugging is on\n");
+
+ cmd_show_lib_debugs(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_rip_events,
+ debug_rip_events_cmd,
+ "debug rip events",
+ DEBUG_STR
+ RIP_STR
+ "RIP events\n")
+{
+ rip_debug_event = RIP_DEBUG_EVENT;
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_rip_packet,
+ debug_rip_packet_cmd,
+ "debug rip packet",
+ DEBUG_STR
+ RIP_STR
+ "RIP packet\n")
+{
+ rip_debug_packet = RIP_DEBUG_PACKET;
+ rip_debug_packet |= RIP_DEBUG_SEND;
+ rip_debug_packet |= RIP_DEBUG_RECV;
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_rip_packet_direct,
+ debug_rip_packet_direct_cmd,
+ "debug rip packet <recv|send>",
+ DEBUG_STR
+ RIP_STR
+ "RIP packet\n"
+ "RIP receive packet\n"
+ "RIP send packet\n")
+{
+ int idx_recv_send = 3;
+ rip_debug_packet |= RIP_DEBUG_PACKET;
+ if (strcmp("send", argv[idx_recv_send]->text) == 0)
+ rip_debug_packet |= RIP_DEBUG_SEND;
+ if (strcmp("recv", argv[idx_recv_send]->text) == 0)
+ rip_debug_packet |= RIP_DEBUG_RECV;
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_rip_zebra,
+ debug_rip_zebra_cmd,
+ "debug rip zebra",
+ DEBUG_STR
+ RIP_STR
+ "RIP and ZEBRA communication\n")
+{
+ rip_debug_zebra = RIP_DEBUG_ZEBRA;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_rip_events,
+ no_debug_rip_events_cmd,
+ "no debug rip events",
+ NO_STR
+ DEBUG_STR
+ RIP_STR
+ "RIP events\n")
+{
+ rip_debug_event = 0;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_rip_packet,
+ no_debug_rip_packet_cmd,
+ "no debug rip packet",
+ NO_STR
+ DEBUG_STR
+ RIP_STR
+ "RIP packet\n")
+{
+ rip_debug_packet = 0;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_rip_packet_direct,
+ no_debug_rip_packet_direct_cmd,
+ "no debug rip packet <recv|send>",
+ NO_STR
+ DEBUG_STR
+ RIP_STR
+ "RIP packet\n"
+ "RIP option set for receive packet\n"
+ "RIP option set for send packet\n")
+{
+ int idx_recv_send = 4;
+ if (strcmp("send", argv[idx_recv_send]->text) == 0) {
+ if (IS_RIP_DEBUG_RECV)
+ rip_debug_packet &= ~RIP_DEBUG_SEND;
+ else
+ rip_debug_packet = 0;
+ } else if (strcmp("recv", argv[idx_recv_send]->text) == 0) {
+ if (IS_RIP_DEBUG_SEND)
+ rip_debug_packet &= ~RIP_DEBUG_RECV;
+ else
+ rip_debug_packet = 0;
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_rip_zebra,
+ no_debug_rip_zebra_cmd,
+ "no debug rip zebra",
+ NO_STR
+ DEBUG_STR
+ RIP_STR
+ "RIP and ZEBRA communication\n")
+{
+ rip_debug_zebra = 0;
+ return CMD_SUCCESS;
+}
+
+static int config_write_debug(struct vty *vty);
+/* Debug node. */
+static struct cmd_node debug_node = {
+ .name = "debug",
+ .node = DEBUG_NODE,
+ .prompt = "",
+ .config_write = config_write_debug,
+};
+
+static int config_write_debug(struct vty *vty)
+{
+ int write = 0;
+
+ if (IS_RIP_DEBUG_EVENT) {
+ vty_out(vty, "debug rip events\n");
+ write++;
+ }
+ if (IS_RIP_DEBUG_PACKET) {
+ if (IS_RIP_DEBUG_SEND && IS_RIP_DEBUG_RECV) {
+ vty_out(vty, "debug rip packet\n");
+ write++;
+ } else {
+ if (IS_RIP_DEBUG_SEND)
+ vty_out(vty, "debug rip packet send\n");
+ else
+ vty_out(vty, "debug rip packet recv\n");
+ write++;
+ }
+ }
+ if (IS_RIP_DEBUG_ZEBRA) {
+ vty_out(vty, "debug rip zebra\n");
+ write++;
+ }
+ return write;
+}
+
+void rip_debug_init(void)
+{
+ rip_debug_event = 0;
+ rip_debug_packet = 0;
+ rip_debug_zebra = 0;
+
+ install_node(&debug_node);
+
+ install_element(ENABLE_NODE, &show_debugging_rip_cmd);
+ install_element(ENABLE_NODE, &debug_rip_events_cmd);
+ install_element(ENABLE_NODE, &debug_rip_packet_cmd);
+ install_element(ENABLE_NODE, &debug_rip_packet_direct_cmd);
+ install_element(ENABLE_NODE, &debug_rip_zebra_cmd);
+ install_element(ENABLE_NODE, &no_debug_rip_events_cmd);
+ install_element(ENABLE_NODE, &no_debug_rip_packet_cmd);
+ install_element(ENABLE_NODE, &no_debug_rip_packet_direct_cmd);
+ install_element(ENABLE_NODE, &no_debug_rip_zebra_cmd);
+
+ install_element(CONFIG_NODE, &debug_rip_events_cmd);
+ install_element(CONFIG_NODE, &debug_rip_packet_cmd);
+ install_element(CONFIG_NODE, &debug_rip_packet_direct_cmd);
+ install_element(CONFIG_NODE, &debug_rip_zebra_cmd);
+ install_element(CONFIG_NODE, &no_debug_rip_events_cmd);
+ install_element(CONFIG_NODE, &no_debug_rip_packet_cmd);
+ install_element(CONFIG_NODE, &no_debug_rip_packet_direct_cmd);
+ install_element(CONFIG_NODE, &no_debug_rip_zebra_cmd);
+}
diff --git a/ripd/rip_debug.h b/ripd/rip_debug.h
new file mode 100644
index 0000000..ca9188f
--- /dev/null
+++ b/ripd/rip_debug.h
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RIP debug routines
+ * Copyright (C) 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
+ */
+
+#ifndef _ZEBRA_RIP_DEBUG_H
+#define _ZEBRA_RIP_DEBUG_H
+
+/* RIP debug event flags. */
+#define RIP_DEBUG_EVENT 0x01
+
+/* RIP debug packet flags. */
+#define RIP_DEBUG_PACKET 0x01
+#define RIP_DEBUG_SEND 0x20
+#define RIP_DEBUG_RECV 0x40
+#define RIP_DEBUG_DETAIL 0x80
+
+/* RIP debug zebra flags. */
+#define RIP_DEBUG_ZEBRA 0x01
+
+/* Debug related macro. */
+#define IS_RIP_DEBUG_EVENT (rip_debug_event & RIP_DEBUG_EVENT)
+
+#define IS_RIP_DEBUG_PACKET (rip_debug_packet & RIP_DEBUG_PACKET)
+#define IS_RIP_DEBUG_SEND (rip_debug_packet & RIP_DEBUG_SEND)
+#define IS_RIP_DEBUG_RECV (rip_debug_packet & RIP_DEBUG_RECV)
+
+#define IS_RIP_DEBUG_ZEBRA (rip_debug_zebra & RIP_DEBUG_ZEBRA)
+
+extern unsigned long rip_debug_event;
+extern unsigned long rip_debug_packet;
+extern unsigned long rip_debug_zebra;
+
+extern void rip_debug_init(void);
+
+#endif /* _ZEBRA_RIP_DEBUG_H */
diff --git a/ripd/rip_errors.c b/ripd/rip_errors.c
new file mode 100644
index 0000000..483ea77
--- /dev/null
+++ b/ripd/rip_errors.c
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RIP-specific error messages.
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+
+#include <zebra.h>
+
+#include "lib/ferr.h"
+#include "rip_errors.h"
+
+static struct log_ref ferr_rip_err[] = {
+ {.code = EC_RIP_PACKET,
+ .title = "RIP Packet Error",
+ .description = "RIP has detected a packet encode/decode issue",
+ .suggestion = "Gather log files from both sides and open a Issue"},
+ {
+ .code = END_FERR,
+ }};
+
+void rip_error_init(void)
+{
+ log_ref_add(ferr_rip_err);
+}
diff --git a/ripd/rip_errors.h b/ripd/rip_errors.h
new file mode 100644
index 0000000..93b4474
--- /dev/null
+++ b/ripd/rip_errors.h
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RIP-specific error messages.
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+
+#ifndef __RIP_ERRORS_H__
+#define __RIP_ERRORS_H__
+
+#include "lib/ferr.h"
+
+enum rip_log_refs {
+ EC_RIP_PACKET = RIP_FERR_START,
+ RIP_ERR_CONFIG,
+};
+
+extern void rip_error_init(void);
+
+#endif
diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c
new file mode 100644
index 0000000..505290e
--- /dev/null
+++ b/ripd/rip_interface.c
@@ -0,0 +1,1123 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Interface related function for RIP.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro <kunihiro@zebra.org>
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "if.h"
+#include "sockunion.h"
+#include "prefix.h"
+#include "memory.h"
+#include "network.h"
+#include "table.h"
+#include "log.h"
+#include "stream.h"
+#include "frrevent.h"
+#include "zclient.h"
+#include "filter.h"
+#include "sockopt.h"
+#include "privs.h"
+#include "lib_errors.h"
+#include "northbound_cli.h"
+
+#include "zebra/connected.h"
+
+#include "ripd/ripd.h"
+#include "ripd/rip_bfd.h"
+#include "ripd/rip_debug.h"
+#include "ripd/rip_interface.h"
+
+DEFINE_MTYPE_STATIC(RIPD, RIP_INTERFACE, "RIP interface");
+DEFINE_MTYPE(RIPD, RIP_INTERFACE_STRING, "RIP Interface String");
+DEFINE_HOOK(rip_ifaddr_add, (struct connected * ifc), (ifc));
+DEFINE_HOOK(rip_ifaddr_del, (struct connected * ifc), (ifc));
+
+/* static prototypes */
+static void rip_enable_apply(struct interface *);
+static void rip_passive_interface_apply(struct interface *);
+static int rip_if_down(struct interface *ifp);
+static int rip_enable_if_lookup(struct rip *rip, const char *ifname);
+static int rip_enable_network_lookup2(struct connected *connected);
+static void rip_enable_apply_all(struct rip *rip);
+
+const struct message ri_version_msg[] = {{RI_RIP_VERSION_1, "1"},
+ {RI_RIP_VERSION_2, "2"},
+ {RI_RIP_VERSION_1_AND_2, "1 2"},
+ {RI_RIP_VERSION_NONE, "none"},
+ {0}};
+
+/* Join to the RIP version 2 multicast group. */
+static int ipv4_multicast_join(int sock, struct in_addr group,
+ struct in_addr ifa, ifindex_t ifindex)
+{
+ int ret;
+
+ ret = setsockopt_ipv4_multicast(sock, IP_ADD_MEMBERSHIP, ifa,
+ group.s_addr, ifindex);
+
+ if (ret < 0)
+ zlog_info("can't setsockopt IP_ADD_MEMBERSHIP %s",
+ safe_strerror(errno));
+
+ return ret;
+}
+
+/* Leave from the RIP version 2 multicast group. */
+static int ipv4_multicast_leave(int sock, struct in_addr group,
+ struct in_addr ifa, ifindex_t ifindex)
+{
+ int ret;
+
+ ret = setsockopt_ipv4_multicast(sock, IP_DROP_MEMBERSHIP, ifa,
+ group.s_addr, ifindex);
+
+ if (ret < 0)
+ zlog_info("can't setsockopt IP_DROP_MEMBERSHIP");
+
+ return ret;
+}
+
+static void rip_interface_reset(struct rip_interface *);
+
+/* Allocate new RIP's interface configuration. */
+static struct rip_interface *rip_interface_new(void)
+{
+ struct rip_interface *ri;
+
+ ri = XCALLOC(MTYPE_RIP_INTERFACE, sizeof(struct rip_interface));
+
+ rip_interface_reset(ri);
+
+ return ri;
+}
+
+void rip_interface_multicast_set(int sock, struct connected *connected)
+{
+ struct in_addr addr;
+
+ assert(connected != NULL);
+
+ addr = CONNECTED_ID(connected)->u.prefix4;
+
+ if (setsockopt_ipv4_multicast_if(sock, addr, connected->ifp->ifindex)
+ < 0) {
+ zlog_warn(
+ "Can't setsockopt IP_MULTICAST_IF on fd %d to ifindex %d for interface %s",
+ sock, connected->ifp->ifindex, connected->ifp->name);
+ }
+
+ return;
+}
+
+/* Send RIP request packet to specified interface. */
+static void rip_request_interface_send(struct interface *ifp, uint8_t version)
+{
+ struct sockaddr_in to;
+
+ /* RIPv2 support multicast. */
+ if (version == RIPv2 && if_is_multicast(ifp)) {
+
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("multicast request on %s", ifp->name);
+
+ rip_request_send(NULL, ifp, version, NULL);
+ return;
+ }
+
+ /* RIPv1 and non multicast interface. */
+ if (if_is_pointopoint(ifp) || if_is_broadcast(ifp)) {
+ struct listnode *cnode, *cnnode;
+ struct connected *connected;
+
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("broadcast request to %s", ifp->name);
+
+ for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode,
+ connected)) {
+ if (connected->address->family != AF_INET)
+ continue;
+
+ memset(&to, 0, sizeof(struct sockaddr_in));
+ to.sin_port = htons(RIP_PORT_DEFAULT);
+ if (connected->destination)
+ /* use specified broadcast or peer
+ * destination addr */
+ to.sin_addr = connected->destination->u.prefix4;
+ else if (connected->address->prefixlen
+ < IPV4_MAX_BITLEN)
+ /* calculate the appropriate broadcast
+ * address */
+ to.sin_addr.s_addr = ipv4_broadcast_addr(
+ connected->address->u.prefix4.s_addr,
+ connected->address->prefixlen);
+ else
+ /* do not know where to send the packet
+ */
+ continue;
+
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("SEND request to %pI4",
+ &to.sin_addr);
+
+ rip_request_send(&to, ifp, version, connected);
+ }
+ }
+}
+
+/* This will be executed when interface goes up. */
+static void rip_request_interface(struct interface *ifp)
+{
+ struct rip_interface *ri;
+ int vsend;
+
+ /* In default ripd doesn't send RIP_REQUEST to the loopback interface.
+ */
+ if (if_is_loopback(ifp))
+ return;
+
+ /* If interface is down, don't send RIP packet. */
+ if (!if_is_operative(ifp))
+ return;
+
+ /* Fetch RIP interface information. */
+ ri = ifp->info;
+
+ /* If there is no version configuration in the interface,
+ use rip's version setting. */
+ vsend = ((ri->ri_send == RI_RIP_UNSPEC) ? ri->rip->version_send
+ : ri->ri_send);
+ if (vsend & RIPv1)
+ rip_request_interface_send(ifp, RIPv1);
+ if (vsend & RIPv2)
+ rip_request_interface_send(ifp, RIPv2);
+}
+
+/* Multicast packet receive socket. */
+static int rip_multicast_join(struct interface *ifp, int sock)
+{
+ struct listnode *cnode;
+ struct connected *ifc;
+
+ if (if_is_operative(ifp) && if_is_multicast(ifp)) {
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("multicast join at %s", ifp->name);
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, ifc)) {
+ struct prefix_ipv4 *p;
+ struct in_addr group;
+
+ p = (struct prefix_ipv4 *)ifc->address;
+
+ if (p->family != AF_INET)
+ continue;
+
+ group.s_addr = htonl(INADDR_RIP_GROUP);
+ if (ipv4_multicast_join(sock, group, p->prefix,
+ ifp->ifindex)
+ < 0)
+ return -1;
+ else
+ return 0;
+ }
+ }
+ return 0;
+}
+
+/* Leave from multicast group. */
+static void rip_multicast_leave(struct interface *ifp, int sock)
+{
+ struct listnode *cnode;
+ struct connected *connected;
+
+ if (if_is_up(ifp) && if_is_multicast(ifp)) {
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("multicast leave from %s", ifp->name);
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) {
+ struct prefix_ipv4 *p;
+ struct in_addr group;
+
+ p = (struct prefix_ipv4 *)connected->address;
+
+ if (p->family != AF_INET)
+ continue;
+
+ group.s_addr = htonl(INADDR_RIP_GROUP);
+ if (ipv4_multicast_leave(sock, group, p->prefix,
+ ifp->ifindex)
+ == 0)
+ return;
+ }
+ }
+}
+
+/* Is there and address on interface that I could use ? */
+static int rip_if_ipv4_address_check(struct interface *ifp)
+{
+ struct listnode *nn;
+ struct connected *connected;
+ int count = 0;
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, nn, connected)) {
+ struct prefix *p;
+
+ p = connected->address;
+
+ if (p->family == AF_INET)
+ count++;
+ }
+
+ return count;
+}
+
+
+/* Does this address belongs to me ? */
+int if_check_address(struct rip *rip, struct in_addr addr)
+{
+ struct interface *ifp;
+
+ FOR_ALL_INTERFACES (rip->vrf, ifp) {
+ struct listnode *cnode;
+ struct connected *connected;
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) {
+ struct prefix_ipv4 *p;
+
+ p = (struct prefix_ipv4 *)connected->address;
+
+ if (p->family != AF_INET)
+ continue;
+
+ if (IPV4_ADDR_CMP(&p->prefix, &addr) == 0)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Interface link down message processing. */
+static int rip_ifp_down(struct interface *ifp)
+{
+ rip_interface_sync(ifp);
+ rip_if_down(ifp);
+
+ if (IS_RIP_DEBUG_ZEBRA)
+ zlog_debug(
+ "interface %s vrf %s(%u) index %d flags %llx metric %d mtu %d is down",
+ ifp->name, ifp->vrf->name, ifp->vrf->vrf_id,
+ ifp->ifindex, (unsigned long long)ifp->flags,
+ ifp->metric, ifp->mtu);
+
+ return 0;
+}
+
+/* Interface link up message processing */
+static int rip_ifp_up(struct interface *ifp)
+{
+ if (IS_RIP_DEBUG_ZEBRA)
+ zlog_debug(
+ "interface %s vrf %s(%u) index %d flags %#llx metric %d mtu %d is up",
+ ifp->name, ifp->vrf->name, ifp->vrf->vrf_id,
+ ifp->ifindex, (unsigned long long)ifp->flags,
+ ifp->metric, ifp->mtu);
+
+ rip_interface_sync(ifp);
+
+ /* Check if this interface is RIP enabled or not.*/
+ rip_enable_apply(ifp);
+
+ /* Check for a passive interface */
+ rip_passive_interface_apply(ifp);
+
+ /* Apply distribute list to the all interface. */
+ rip_distribute_update_interface(ifp);
+
+ return 0;
+}
+
+/* Interface addition message from zebra. */
+static int rip_ifp_create(struct interface *ifp)
+{
+ rip_interface_sync(ifp);
+
+ if (IS_RIP_DEBUG_ZEBRA)
+ zlog_debug(
+ "interface add %s vrf %s(%u) index %d flags %#llx metric %d mtu %d",
+ ifp->name, ifp->vrf->name, ifp->vrf->vrf_id,
+ ifp->ifindex, (unsigned long long)ifp->flags,
+ ifp->metric, ifp->mtu);
+
+ /* Check if this interface is RIP enabled or not.*/
+ rip_enable_apply(ifp);
+
+ /* Check for a passive interface */
+ rip_passive_interface_apply(ifp);
+
+ /* Apply distribute list to the all interface. */
+ rip_distribute_update_interface(ifp);
+
+ /* rip_request_neighbor_all (); */
+
+ /* Check interface routemap. */
+ rip_if_rmap_update_interface(ifp);
+
+ return 0;
+}
+
+static int rip_ifp_destroy(struct interface *ifp)
+{
+ rip_interface_sync(ifp);
+ if (if_is_up(ifp)) {
+ rip_if_down(ifp);
+ }
+
+ if (IS_RIP_DEBUG_ZEBRA)
+ zlog_debug(
+ "interface delete %s vrf %s(%u) index %d flags %#llx metric %d mtu %d",
+ ifp->name, ifp->vrf->name, ifp->vrf->vrf_id,
+ ifp->ifindex, (unsigned long long)ifp->flags,
+ ifp->metric, ifp->mtu);
+
+ return 0;
+}
+
+static void rip_interface_clean(struct rip_interface *ri)
+{
+ ri->enable_network = 0;
+ ri->enable_interface = 0;
+ ri->running = 0;
+
+ EVENT_OFF(ri->t_wakeup);
+}
+
+void rip_interfaces_clean(struct rip *rip)
+{
+ struct interface *ifp;
+
+ FOR_ALL_INTERFACES (rip->vrf, ifp)
+ rip_interface_clean(ifp->info);
+}
+
+static void rip_interface_reset(struct rip_interface *ri)
+{
+ ri->auth_type = yang_get_default_enum("%s/authentication-scheme/mode",
+ RIP_IFACE);
+ ri->md5_auth_len = yang_get_default_enum(
+ "%s/authentication-scheme/md5-auth-length", RIP_IFACE);
+
+ /* Set default split-horizon behavior. If the interface is Frame
+ Relay or SMDS is enabled, the default value for split-horizon is
+ off. But currently Zebra does detect Frame Relay or SMDS
+ interface. So all interface is set to split horizon. */
+ ri->split_horizon =
+ yang_get_default_enum("%s/split-horizon", RIP_IFACE);
+
+ ri->ri_send = yang_get_default_enum("%s/version-send", RIP_IFACE);
+ ri->ri_receive = yang_get_default_enum("%s/version-receive", RIP_IFACE);
+ ri->v2_broadcast = yang_get_default_bool("%s/v2-broadcast", RIP_IFACE);
+
+ XFREE(MTYPE_RIP_INTERFACE_STRING, ri->auth_str);
+
+ XFREE(MTYPE_RIP_INTERFACE_STRING, ri->key_chain);
+
+ ri->list[RIP_FILTER_IN] = NULL;
+ ri->list[RIP_FILTER_OUT] = NULL;
+
+ ri->prefix[RIP_FILTER_IN] = NULL;
+ ri->prefix[RIP_FILTER_OUT] = NULL;
+
+ ri->recv_badpackets = 0;
+ ri->recv_badroutes = 0;
+ ri->sent_updates = 0;
+
+ ri->passive = 0;
+ XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile);
+
+ rip_interface_clean(ri);
+}
+
+int rip_if_down(struct interface *ifp)
+{
+ struct rip *rip;
+ struct route_node *rp;
+ struct rip_info *rinfo;
+ struct rip_interface *ri = NULL;
+ struct list *list = NULL;
+ struct listnode *listnode = NULL, *nextnode = NULL;
+
+ ri = ifp->info;
+
+ EVENT_OFF(ri->t_wakeup);
+
+ rip = ri->rip;
+ if (rip) {
+ for (rp = route_top(rip->table); rp; rp = route_next(rp))
+ if ((list = rp->info) != NULL)
+ for (ALL_LIST_ELEMENTS(list, listnode, nextnode,
+ rinfo))
+ if (rinfo->nh.ifindex == ifp->ifindex)
+ rip_ecmp_delete(rip, rinfo);
+
+ if (ri->running) {
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("turn off %s", ifp->name);
+
+ /* Leave from multicast group. */
+ rip_multicast_leave(ifp, rip->sock);
+
+ ri->running = 0;
+ }
+ }
+
+ return 0;
+}
+
+static void rip_apply_address_add(struct connected *ifc)
+{
+ struct rip_interface *ri = ifc->ifp->info;
+ struct rip *rip = ri->rip;
+ struct prefix_ipv4 address;
+ struct nexthop nh;
+ struct prefix *p;
+
+ if (!rip)
+ return;
+
+ if (!if_is_up(ifc->ifp))
+ return;
+
+ p = ifc->address;
+
+ memset(&address, 0, sizeof(address));
+ memset(&nh, 0, sizeof(nh));
+
+ address.family = p->family;
+ address.prefix = p->u.prefix4;
+ address.prefixlen = p->prefixlen;
+ apply_mask_ipv4(&address);
+
+ nh.ifindex = ifc->ifp->ifindex;
+ nh.type = NEXTHOP_TYPE_IFINDEX;
+
+ /* Check if this interface is RIP enabled or not
+ or Check if this address's prefix is RIP enabled */
+ if ((rip_enable_if_lookup(rip, ifc->ifp->name) >= 0)
+ || (rip_enable_network_lookup2(ifc) >= 0))
+ rip_redistribute_add(rip, ZEBRA_ROUTE_CONNECT,
+ RIP_ROUTE_INTERFACE, &address, &nh, 0, 0,
+ 0);
+}
+
+int rip_interface_address_add(ZAPI_CALLBACK_ARGS)
+{
+ struct connected *ifc;
+ struct prefix *p;
+
+ ifc = zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD,
+ zclient->ibuf, vrf_id);
+
+ if (ifc == NULL)
+ return 0;
+
+ p = ifc->address;
+
+ if (p->family == AF_INET) {
+ if (IS_RIP_DEBUG_ZEBRA)
+ zlog_debug("connected address %pFX is added", p);
+
+ rip_enable_apply(ifc->ifp);
+ /* Check if this prefix needs to be redistributed */
+ rip_apply_address_add(ifc);
+
+ hook_call(rip_ifaddr_add, ifc);
+ }
+
+ return 0;
+}
+
+static void rip_apply_address_del(struct connected *ifc)
+{
+ struct rip_interface *ri = ifc->ifp->info;
+ struct rip *rip = ri->rip;
+ struct prefix_ipv4 address;
+ struct prefix *p;
+
+ if (!rip)
+ return;
+
+ if (!if_is_up(ifc->ifp))
+ return;
+
+ p = ifc->address;
+
+ memset(&address, 0, sizeof(address));
+ address.family = p->family;
+ address.prefix = p->u.prefix4;
+ address.prefixlen = p->prefixlen;
+ apply_mask_ipv4(&address);
+
+ rip_redistribute_delete(rip, ZEBRA_ROUTE_CONNECT, RIP_ROUTE_INTERFACE,
+ &address, ifc->ifp->ifindex);
+}
+
+int rip_interface_address_delete(ZAPI_CALLBACK_ARGS)
+{
+ struct connected *ifc;
+ struct prefix *p;
+
+ ifc = zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_DELETE,
+ zclient->ibuf, vrf_id);
+
+ if (ifc) {
+ p = ifc->address;
+ if (p->family == AF_INET) {
+ if (IS_RIP_DEBUG_ZEBRA)
+ zlog_debug("connected address %pFX is deleted",
+ p);
+
+ hook_call(rip_ifaddr_del, ifc);
+
+ /* Chech whether this prefix needs to be removed */
+ rip_apply_address_del(ifc);
+ }
+
+ connected_free(&ifc);
+ }
+
+ return 0;
+}
+
+/* Check interface is enabled by network statement. */
+/* Check whether the interface has at least a connected prefix that
+ * is within the ripng_enable_network table. */
+static int rip_enable_network_lookup_if(struct interface *ifp)
+{
+ struct rip_interface *ri = ifp->info;
+ struct rip *rip = ri->rip;
+ struct listnode *node, *nnode;
+ struct connected *connected;
+ struct prefix_ipv4 address;
+
+ if (!rip)
+ return -1;
+
+ for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, connected)) {
+ struct prefix *p;
+ struct route_node *n;
+
+ p = connected->address;
+
+ if (p->family == AF_INET) {
+ address.family = AF_INET;
+ address.prefix = p->u.prefix4;
+ address.prefixlen = IPV4_MAX_BITLEN;
+
+ n = route_node_match(rip->enable_network,
+ (struct prefix *)&address);
+ if (n) {
+ route_unlock_node(n);
+ return 1;
+ }
+ }
+ }
+ return -1;
+}
+
+/* Check whether connected is within the ripng_enable_network table. */
+static int rip_enable_network_lookup2(struct connected *connected)
+{
+ struct rip_interface *ri = connected->ifp->info;
+ struct rip *rip = ri->rip;
+ struct prefix_ipv4 address;
+ struct prefix *p;
+
+ p = connected->address;
+
+ if (p->family == AF_INET) {
+ struct route_node *node;
+
+ address.family = p->family;
+ address.prefix = p->u.prefix4;
+ address.prefixlen = IPV4_MAX_BITLEN;
+
+ /* LPM on p->family, p->u.prefix4/IPV4_MAX_BITLEN within
+ * rip->enable_network */
+ node = route_node_match(rip->enable_network,
+ (struct prefix *)&address);
+
+ if (node) {
+ route_unlock_node(node);
+ return 1;
+ }
+ }
+
+ return -1;
+}
+/* Add RIP enable network. */
+int rip_enable_network_add(struct rip *rip, struct prefix *p)
+{
+ struct route_node *node;
+
+ node = route_node_get(rip->enable_network, p);
+
+ if (node->info) {
+ route_unlock_node(node);
+ return NB_ERR_INCONSISTENCY;
+ } else
+ node->info = (void *)1;
+
+ /* XXX: One should find a better solution than a generic one */
+ rip_enable_apply_all(rip);
+
+ return NB_OK;
+}
+
+/* Delete RIP enable network. */
+int rip_enable_network_delete(struct rip *rip, struct prefix *p)
+{
+ struct route_node *node;
+
+ node = route_node_lookup(rip->enable_network, p);
+ if (node) {
+ node->info = NULL;
+
+ /* Unlock info lock. */
+ route_unlock_node(node);
+
+ /* Unlock lookup lock. */
+ route_unlock_node(node);
+
+ /* XXX: One should find a better solution than a generic one */
+ rip_enable_apply_all(rip);
+
+ return NB_OK;
+ }
+
+ return NB_ERR_INCONSISTENCY;
+}
+
+/* Check interface is enabled by ifname statement. */
+static int rip_enable_if_lookup(struct rip *rip, const char *ifname)
+{
+ unsigned int i;
+ char *str;
+
+ if (!rip)
+ return -1;
+
+ for (i = 0; i < vector_active(rip->enable_interface); i++)
+ if ((str = vector_slot(rip->enable_interface, i)) != NULL)
+ if (strcmp(str, ifname) == 0)
+ return i;
+ return -1;
+}
+
+/* Add interface to rip_enable_if. */
+int rip_enable_if_add(struct rip *rip, const char *ifname)
+{
+ int ret;
+
+ ret = rip_enable_if_lookup(rip, ifname);
+ if (ret >= 0)
+ return NB_ERR_INCONSISTENCY;
+
+ vector_set(rip->enable_interface,
+ XSTRDUP(MTYPE_RIP_INTERFACE_STRING, ifname));
+
+ rip_enable_apply_all(rip); /* TODOVJ */
+
+ return NB_OK;
+}
+
+/* Delete interface from rip_enable_if. */
+int rip_enable_if_delete(struct rip *rip, const char *ifname)
+{
+ int index;
+ char *str;
+
+ index = rip_enable_if_lookup(rip, ifname);
+ if (index < 0)
+ return NB_ERR_INCONSISTENCY;
+
+ str = vector_slot(rip->enable_interface, index);
+ XFREE(MTYPE_RIP_INTERFACE_STRING, str);
+ vector_unset(rip->enable_interface, index);
+
+ rip_enable_apply_all(rip); /* TODOVJ */
+
+ return NB_OK;
+}
+
+/* Join to multicast group and send request to the interface. */
+static void rip_interface_wakeup(struct event *t)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ /* Get interface. */
+ ifp = EVENT_ARG(t);
+
+ ri = ifp->info;
+
+ /* Join to multicast group. */
+ if (rip_multicast_join(ifp, ri->rip->sock) < 0) {
+ flog_err_sys(EC_LIB_SOCKET,
+ "multicast join failed, interface %s not running",
+ ifp->name);
+ return;
+ }
+
+ /* Set running flag. */
+ ri->running = 1;
+
+ /* Send RIP request to the interface. */
+ rip_request_interface(ifp);
+}
+
+static void rip_connect_set(struct interface *ifp, int set)
+{
+ struct rip_interface *ri = ifp->info;
+ struct rip *rip = ri->rip;
+ struct listnode *node, *nnode;
+ struct connected *connected;
+ struct prefix_ipv4 address;
+ struct nexthop nh;
+
+ memset(&nh, 0, sizeof(nh));
+
+ for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, connected)) {
+ struct prefix *p;
+ p = connected->address;
+
+ if (p->family != AF_INET)
+ continue;
+
+ address.family = AF_INET;
+ address.prefix = p->u.prefix4;
+ address.prefixlen = p->prefixlen;
+ apply_mask_ipv4(&address);
+
+ nh.ifindex = connected->ifp->ifindex;
+ nh.type = NEXTHOP_TYPE_IFINDEX;
+ if (set) {
+ /* Check once more whether this prefix is within a
+ * "network IF_OR_PREF" one */
+ if ((rip_enable_if_lookup(rip, connected->ifp->name)
+ >= 0)
+ || (rip_enable_network_lookup2(connected) >= 0))
+ rip_redistribute_add(rip, ZEBRA_ROUTE_CONNECT,
+ RIP_ROUTE_INTERFACE,
+ &address, &nh, 0, 0, 0);
+ } else {
+ rip_redistribute_delete(rip, ZEBRA_ROUTE_CONNECT,
+ RIP_ROUTE_INTERFACE, &address,
+ connected->ifp->ifindex);
+ if (rip_redistribute_check(rip, ZEBRA_ROUTE_CONNECT))
+ rip_redistribute_add(rip, ZEBRA_ROUTE_CONNECT,
+ RIP_ROUTE_REDISTRIBUTE,
+ &address, &nh, 0, 0, 0);
+ }
+ }
+}
+
+/* Update interface status. */
+void rip_enable_apply(struct interface *ifp)
+{
+ int ret;
+ struct rip_interface *ri = NULL;
+
+ /* Check interface. */
+ if (!if_is_operative(ifp))
+ return;
+
+ ri = ifp->info;
+
+ /* Check network configuration. */
+ ret = rip_enable_network_lookup_if(ifp);
+
+ /* If the interface is matched. */
+ if (ret > 0)
+ ri->enable_network = 1;
+ else
+ ri->enable_network = 0;
+
+ /* Check interface name configuration. */
+ ret = rip_enable_if_lookup(ri->rip, ifp->name);
+ if (ret >= 0)
+ ri->enable_interface = 1;
+ else
+ ri->enable_interface = 0;
+
+ /* any interface MUST have an IPv4 address */
+ if (!rip_if_ipv4_address_check(ifp)) {
+ ri->enable_network = 0;
+ ri->enable_interface = 0;
+ }
+
+ /* Update running status of the interface. */
+ if (ri->enable_network || ri->enable_interface) {
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("turn on %s", ifp->name);
+
+ /* Add interface wake up thread. */
+ event_add_timer(master, rip_interface_wakeup, ifp, 1,
+ &ri->t_wakeup);
+ rip_connect_set(ifp, 1);
+ } else if (ri->running) {
+ /* Might as well clean up the route table as well
+ * rip_if_down sets to 0 ri->running, and displays "turn
+ *off %s"
+ **/
+ rip_if_down(ifp);
+
+ rip_connect_set(ifp, 0);
+ }
+}
+
+/* Apply network configuration to all interface. */
+static void rip_enable_apply_all(struct rip *rip)
+{
+ struct interface *ifp;
+
+ /* Check each interface. */
+ FOR_ALL_INTERFACES (rip->vrf, ifp)
+ rip_enable_apply(ifp);
+}
+
+int rip_neighbor_lookup(struct rip *rip, struct sockaddr_in *from)
+{
+ struct prefix_ipv4 p;
+ struct route_node *node;
+
+ memset(&p, 0, sizeof(p));
+ p.family = AF_INET;
+ p.prefix = from->sin_addr;
+ p.prefixlen = IPV4_MAX_BITLEN;
+
+ node = route_node_lookup(rip->neighbor, (struct prefix *)&p);
+ if (node) {
+ route_unlock_node(node);
+ return 1;
+ }
+ return 0;
+}
+
+/* Add new RIP neighbor to the neighbor tree. */
+int rip_neighbor_add(struct rip *rip, struct prefix_ipv4 *p)
+{
+ struct route_node *node;
+
+ node = route_node_get(rip->neighbor, (struct prefix *)p);
+
+ if (node->info)
+ return NB_ERR_INCONSISTENCY;
+
+ node->info = rip->neighbor;
+
+ return NB_OK;
+}
+
+/* Delete RIP neighbor from the neighbor tree. */
+int rip_neighbor_delete(struct rip *rip, struct prefix_ipv4 *p)
+{
+ struct route_node *node;
+
+ /* Lock for look up. */
+ node = route_node_lookup(rip->neighbor, (struct prefix *)p);
+ if (!node)
+ return NB_ERR_INCONSISTENCY;
+
+ node->info = NULL;
+
+ /* Unlock lookup lock. */
+ route_unlock_node(node);
+
+ /* Unlock real neighbor information lock. */
+ route_unlock_node(node);
+
+ return NB_OK;
+}
+
+/* Clear all network and neighbor configuration. */
+void rip_clean_network(struct rip *rip)
+{
+ unsigned int i;
+ char *str;
+ struct route_node *rn;
+
+ /* rip->enable_network. */
+ for (rn = route_top(rip->enable_network); rn; rn = route_next(rn))
+ if (rn->info) {
+ rn->info = NULL;
+ route_unlock_node(rn);
+ }
+
+ /* rip->enable_interface. */
+ for (i = 0; i < vector_active(rip->enable_interface); i++)
+ if ((str = vector_slot(rip->enable_interface, i)) != NULL) {
+ XFREE(MTYPE_RIP_INTERFACE_STRING, str);
+ vector_slot(rip->enable_interface, i) = NULL;
+ }
+}
+
+/* Utility function for looking up passive interface settings. */
+static int rip_passive_nondefault_lookup(struct rip *rip, const char *ifname)
+{
+ unsigned int i;
+ char *str;
+
+ for (i = 0; i < vector_active(rip->passive_nondefault); i++)
+ if ((str = vector_slot(rip->passive_nondefault, i)) != NULL)
+ if (strcmp(str, ifname) == 0)
+ return i;
+ return -1;
+}
+
+static void rip_passive_interface_apply(struct interface *ifp)
+{
+ struct rip *rip;
+ struct rip_interface *ri;
+
+ ri = ifp->info;
+ rip = ri->rip;
+ if (rip == NULL)
+ return;
+
+ ri->passive = ((rip_passive_nondefault_lookup(rip, ifp->name) < 0)
+ ? rip->passive_default
+ : !rip->passive_default);
+
+ if (IS_RIP_DEBUG_ZEBRA)
+ zlog_debug("interface %s: passive = %d", ifp->name,
+ ri->passive);
+}
+
+static void rip_passive_interface_apply_all(struct rip *rip)
+{
+ struct interface *ifp;
+
+ FOR_ALL_INTERFACES (rip->vrf, ifp)
+ rip_passive_interface_apply(ifp);
+}
+
+/* Passive interface. */
+int rip_passive_nondefault_set(struct rip *rip, const char *ifname)
+{
+ if (rip_passive_nondefault_lookup(rip, ifname) >= 0)
+ /*
+ * Don't return an error, this can happen after changing
+ * 'passive-default'.
+ */
+ return NB_OK;
+
+ vector_set(rip->passive_nondefault,
+ XSTRDUP(MTYPE_RIP_INTERFACE_STRING, ifname));
+
+ rip_passive_interface_apply_all(rip);
+
+ return NB_OK;
+}
+
+int rip_passive_nondefault_unset(struct rip *rip, const char *ifname)
+{
+ int i;
+ char *str;
+
+ i = rip_passive_nondefault_lookup(rip, ifname);
+ if (i < 0)
+ /*
+ * Don't return an error, this can happen after changing
+ * 'passive-default'.
+ */
+ return NB_OK;
+
+ str = vector_slot(rip->passive_nondefault, i);
+ XFREE(MTYPE_RIP_INTERFACE_STRING, str);
+ vector_unset(rip->passive_nondefault, i);
+
+ rip_passive_interface_apply_all(rip);
+
+ return NB_OK;
+}
+
+/* Free all configured RIP passive-interface settings. */
+void rip_passive_nondefault_clean(struct rip *rip)
+{
+ unsigned int i;
+ char *str;
+
+ for (i = 0; i < vector_active(rip->passive_nondefault); i++)
+ if ((str = vector_slot(rip->passive_nondefault, i)) != NULL) {
+ XFREE(MTYPE_RIP_INTERFACE_STRING, str);
+ vector_slot(rip->passive_nondefault, i) = NULL;
+ }
+ rip_passive_interface_apply_all(rip);
+}
+
+int rip_show_network_config(struct vty *vty, struct rip *rip)
+{
+ unsigned int i;
+ char *ifname;
+ struct route_node *node;
+
+ /* Network type RIP enable interface statement. */
+ for (node = route_top(rip->enable_network); node;
+ node = route_next(node))
+ if (node->info)
+ vty_out(vty, " %pFX\n", &node->p);
+
+ /* Interface name RIP enable statement. */
+ for (i = 0; i < vector_active(rip->enable_interface); i++)
+ if ((ifname = vector_slot(rip->enable_interface, i)) != NULL)
+ vty_out(vty, " %s\n", ifname);
+
+ /* RIP neighbors listing. */
+ for (node = route_top(rip->neighbor); node; node = route_next(node))
+ if (node->info)
+ vty_out(vty, " %pI4\n", &node->p.u.prefix4);
+
+ return 0;
+}
+
+void rip_interface_sync(struct interface *ifp)
+{
+ struct rip_interface *ri;
+
+ ri = ifp->info;
+ if (ri) {
+ ri->rip = ifp->vrf->info;
+ ri->ifp = ifp;
+ }
+}
+
+/* Called when interface structure allocated. */
+static int rip_interface_new_hook(struct interface *ifp)
+{
+ ifp->info = rip_interface_new();
+ rip_interface_sync(ifp);
+
+ return 0;
+}
+
+/* Called when interface structure deleted. */
+static int rip_interface_delete_hook(struct interface *ifp)
+{
+ rip_interface_reset(ifp->info);
+ XFREE(MTYPE_RIP_INTERFACE, ifp->info);
+ return 0;
+}
+
+/* Allocate and initialize interface vector. */
+void rip_if_init(void)
+{
+ /* Default initial size of interface vector. */
+ hook_register_prio(if_add, 0, rip_interface_new_hook);
+ hook_register_prio(if_del, 0, rip_interface_delete_hook);
+
+ /* Install interface node. */
+ if_cmd_init_default();
+ if_zapi_callbacks(rip_ifp_create, rip_ifp_up,
+ rip_ifp_down, rip_ifp_destroy);
+}
diff --git a/ripd/rip_interface.h b/ripd/rip_interface.h
new file mode 100644
index 0000000..eee654b
--- /dev/null
+++ b/ripd/rip_interface.h
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RIP interface routines
+ *
+ * This file is part of Quagga
+ */
+
+#ifndef _QUAGGA_RIP_INTERFACE_H
+#define _QUAGGA_RIP_INTERFACE_H
+
+#include "memory.h"
+#include "zclient.h"
+
+DECLARE_MTYPE(RIP_INTERFACE_STRING);
+
+extern int rip_interface_down(int, struct zclient *, zebra_size_t, vrf_id_t);
+extern int rip_interface_up(int, struct zclient *, zebra_size_t, vrf_id_t);
+extern int rip_interface_add(int, struct zclient *, zebra_size_t, vrf_id_t);
+extern int rip_interface_delete(int, struct zclient *, zebra_size_t, vrf_id_t);
+extern int rip_interface_address_add(int, struct zclient *, zebra_size_t,
+ vrf_id_t);
+extern int rip_interface_address_delete(int, struct zclient *, zebra_size_t,
+ vrf_id_t);
+extern int rip_interface_vrf_update(ZAPI_CALLBACK_ARGS);
+extern void rip_interface_sync(struct interface *ifp);
+
+#endif /* _QUAGGA_RIP_INTERFACE_H */
diff --git a/ripd/rip_main.c b/ripd/rip_main.c
new file mode 100644
index 0000000..ac358eb
--- /dev/null
+++ b/ripd/rip_main.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RIPd main routine.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro <kunihiro@zebra.org>
+ */
+
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "getopt.h"
+#include "frrevent.h"
+#include "command.h"
+#include "memory.h"
+#include "prefix.h"
+#include "filter.h"
+#include "keychain.h"
+#include "log.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "zclient.h"
+#include "vrf.h"
+#include "if_rmap.h"
+#include "libfrr.h"
+#include "routemap.h"
+#include "bfd.h"
+
+#include "ripd/ripd.h"
+#include "ripd/rip_bfd.h"
+#include "ripd/rip_nb.h"
+#include "ripd/rip_errors.h"
+
+/* ripd options. */
+static struct option longopts[] = {{0}};
+
+/* ripd privileges */
+zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND, ZCAP_SYS_ADMIN};
+
+uint32_t zebra_ecmp_count = MULTIPATH_NUM;
+
+struct zebra_privs_t ripd_privs = {
+#if defined(FRR_USER)
+ .user = FRR_USER,
+#endif
+#if defined FRR_GROUP
+ .group = FRR_GROUP,
+#endif
+#ifdef VTY_GROUP
+ .vty_group = VTY_GROUP,
+#endif
+ .caps_p = _caps_p,
+ .cap_num_p = array_size(_caps_p),
+ .cap_num_i = 0};
+
+/* Master of threads. */
+struct event_loop *master;
+
+static struct frr_daemon_info ripd_di;
+
+/* SIGHUP handler. */
+static void sighup(void)
+{
+ zlog_info("SIGHUP received");
+
+ /* Reload config file. */
+ vty_read_config(NULL, ripd_di.config_file, config_default);
+}
+
+/* SIGINT handler. */
+static void sigint(void)
+{
+ zlog_notice("Terminating on signal");
+
+ bfd_protocol_integration_set_shutdown(true);
+ rip_vrf_terminate();
+ if_rmap_terminate();
+ rip_zclient_stop();
+ frr_fini();
+
+ exit(0);
+}
+
+/* SIGUSR1 handler. */
+static void sigusr1(void)
+{
+ zlog_rotate();
+}
+
+static struct frr_signal_t ripd_signals[] = {
+ {
+ .signal = SIGHUP,
+ .handler = &sighup,
+ },
+ {
+ .signal = SIGUSR1,
+ .handler = &sigusr1,
+ },
+ {
+ .signal = SIGINT,
+ .handler = &sigint,
+ },
+ {
+ .signal = SIGTERM,
+ .handler = &sigint,
+ },
+};
+
+static const struct frr_yang_module_info *const ripd_yang_modules[] = {
+ &frr_filter_info,
+ &frr_interface_info,
+ &frr_ripd_info,
+ &frr_route_map_info,
+ &frr_vrf_info,
+};
+
+FRR_DAEMON_INFO(ripd, RIP, .vty_port = RIP_VTY_PORT,
+
+ .proghelp = "Implementation of the RIP routing protocol.",
+
+ .signals = ripd_signals, .n_signals = array_size(ripd_signals),
+
+ .privs = &ripd_privs, .yang_modules = ripd_yang_modules,
+ .n_yang_modules = array_size(ripd_yang_modules),
+);
+
+#define DEPRECATED_OPTIONS ""
+
+/* Main routine of ripd. */
+int main(int argc, char **argv)
+{
+ frr_preinit(&ripd_di, argc, argv);
+
+ frr_opt_add("" DEPRECATED_OPTIONS, longopts, "");
+
+ /* Command line option parse. */
+ while (1) {
+ int opt;
+
+ opt = frr_getopt(argc, argv, NULL);
+
+ if (opt && opt < 128 && strchr(DEPRECATED_OPTIONS, opt)) {
+ fprintf(stderr,
+ "The -%c option no longer exists.\nPlease refer to the manual.\n",
+ opt);
+ continue;
+ }
+
+ if (opt == EOF)
+ break;
+
+ switch (opt) {
+ case 0:
+ break;
+ default:
+ frr_help_exit(1);
+ }
+ }
+
+ /* Prepare master thread. */
+ master = frr_init();
+
+ /* Library initialization. */
+ rip_error_init();
+ keychain_init();
+ rip_vrf_init();
+
+ /* RIP related initialization. */
+ rip_init();
+ rip_if_init();
+ rip_cli_init();
+ rip_zclient_init(master);
+ rip_bfd_init(master);
+
+ frr_config_fork();
+ frr_run(master);
+
+ /* Not reached. */
+ return 0;
+}
diff --git a/ripd/rip_nb.c b/ripd/rip_nb.c
new file mode 100644
index 0000000..d11f1e1
--- /dev/null
+++ b/ripd/rip_nb.c
@@ -0,0 +1,469 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ */
+
+#include <zebra.h>
+
+#include "northbound.h"
+#include "libfrr.h"
+
+#include "ripd/rip_nb.h"
+#include "lib/if_rmap.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/if-route-maps/if-route-map",
+ .cbs = {
+ .create = ripd_instance_if_route_maps_if_route_map_create,
+ .destroy = ripd_instance_if_route_maps_if_route_map_destroy,
+ .cli_show = cli_show_if_route_map,
+ }
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/if-route-maps/if-route-map/in-route-map",
+ .cbs = {
+ .modify = ripd_instance_if_route_maps_if_route_map_in_route_map_modify,
+ .destroy = ripd_instance_if_route_maps_if_route_map_in_route_map_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/if-route-maps/if-route-map/out-route-map",
+ .cbs = {
+ .modify = ripd_instance_if_route_maps_if_route_map_out_route_map_modify,
+ .destroy = ripd_instance_if_route_maps_if_route_map_out_route_map_destroy,
+ }
+ },
+ {
+ .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-ripd:ripd/instance/default-bfd-profile",
+ .cbs = {
+ .modify = ripd_instance_default_bfd_profile_modify,
+ .destroy = ripd_instance_default_bfd_profile_destroy,
+ .cli_show = cli_show_ripd_instance_default_bfd_profile,
+ },
+ },
+ {
+ .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-interface:lib/interface/frr-ripd:rip/bfd-monitoring",
+ .cbs = {
+ .create = lib_interface_rip_bfd_create,
+ .destroy = lib_interface_rip_bfd_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/enable",
+ .cbs = {
+ .cli_show = cli_show_ip_rip_bfd_enable,
+ .modify = lib_interface_rip_bfd_enable_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/profile",
+ .cbs = {
+ .cli_show = cli_show_ip_rip_bfd_profile,
+ .modify = lib_interface_rip_bfd_profile_modify,
+ .destroy = lib_interface_rip_bfd_profile_destroy,
+ },
+ },
+ {
+ .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/nexthops/nexthop",
+ .cbs = {
+ .get_next = ripd_instance_state_routes_route_nexthops_nexthop_get_next,
+ }
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/nh-type",
+ .cbs = {
+ .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_nh_type_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/protocol",
+ .cbs = {
+ .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_protocol_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/rip-type",
+ .cbs = {
+ .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_rip_type_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/gateway",
+ .cbs = {
+ .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_gateway_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/interface",
+ .cbs = {
+ .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_interface_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/from",
+ .cbs = {
+ .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_from_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/tag",
+ .cbs = {
+ .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_tag_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/external-metric",
+ .cbs = {
+ .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_external_metric_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/expire-time",
+ .cbs = {
+ .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_expire_time_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 0000000..9929e09
--- /dev/null
+++ b/ripd/rip_nb.h
@@ -0,0 +1,238 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ */
+
+#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(struct nb_cb_create_args *args);
+int ripd_instance_destroy(struct nb_cb_destroy_args *args);
+const void *ripd_instance_get_next(struct nb_cb_get_next_args *args);
+int ripd_instance_get_keys(struct nb_cb_get_keys_args *args);
+const void *ripd_instance_lookup_entry(struct nb_cb_lookup_entry_args *args);
+int ripd_instance_allow_ecmp_modify(struct nb_cb_modify_args *args);
+int ripd_instance_default_information_originate_modify(
+ struct nb_cb_modify_args *args);
+int ripd_instance_default_metric_modify(struct nb_cb_modify_args *args);
+int ripd_instance_distance_default_modify(struct nb_cb_modify_args *args);
+int ripd_instance_distance_source_create(struct nb_cb_create_args *args);
+int ripd_instance_distance_source_destroy(struct nb_cb_destroy_args *args);
+int ripd_instance_distance_source_distance_modify(
+ struct nb_cb_modify_args *args);
+int ripd_instance_distance_source_access_list_modify(
+ struct nb_cb_modify_args *args);
+int ripd_instance_distance_source_access_list_destroy(
+ struct nb_cb_destroy_args *args);
+int ripd_instance_explicit_neighbor_create(struct nb_cb_create_args *args);
+int ripd_instance_explicit_neighbor_destroy(struct nb_cb_destroy_args *args);
+int ripd_instance_network_create(struct nb_cb_create_args *args);
+int ripd_instance_network_destroy(struct nb_cb_destroy_args *args);
+int ripd_instance_interface_create(struct nb_cb_create_args *args);
+int ripd_instance_interface_destroy(struct nb_cb_destroy_args *args);
+int ripd_instance_offset_list_create(struct nb_cb_create_args *args);
+int ripd_instance_offset_list_destroy(struct nb_cb_destroy_args *args);
+int ripd_instance_offset_list_access_list_modify(
+ struct nb_cb_modify_args *args);
+int ripd_instance_offset_list_metric_modify(struct nb_cb_modify_args *args);
+int ripd_instance_passive_default_modify(struct nb_cb_modify_args *args);
+int ripd_instance_passive_interface_create(struct nb_cb_create_args *args);
+int ripd_instance_passive_interface_destroy(struct nb_cb_destroy_args *args);
+int ripd_instance_non_passive_interface_create(struct nb_cb_create_args *args);
+int ripd_instance_non_passive_interface_destroy(
+ struct nb_cb_destroy_args *args);
+int ripd_instance_redistribute_create(struct nb_cb_create_args *args);
+int ripd_instance_redistribute_destroy(struct nb_cb_destroy_args *args);
+int ripd_instance_redistribute_route_map_modify(struct nb_cb_modify_args *args);
+int ripd_instance_redistribute_route_map_destroy(
+ struct nb_cb_destroy_args *args);
+int ripd_instance_redistribute_metric_modify(struct nb_cb_modify_args *args);
+int ripd_instance_redistribute_metric_destroy(struct nb_cb_destroy_args *args);
+int ripd_instance_if_route_maps_if_route_map_create(
+ struct nb_cb_create_args *args);
+int ripd_instance_if_route_maps_if_route_map_destroy(
+ struct nb_cb_destroy_args *args);
+int ripd_instance_if_route_maps_if_route_map_in_route_map_modify(
+ struct nb_cb_modify_args *args);
+int ripd_instance_if_route_maps_if_route_map_in_route_map_destroy(
+ struct nb_cb_destroy_args *args);
+int ripd_instance_if_route_maps_if_route_map_out_route_map_modify(
+ struct nb_cb_modify_args *args);
+int ripd_instance_if_route_maps_if_route_map_out_route_map_destroy(
+ struct nb_cb_destroy_args *args);
+int ripd_instance_static_route_create(struct nb_cb_create_args *args);
+int ripd_instance_static_route_destroy(struct nb_cb_destroy_args *args);
+int ripd_instance_timers_flush_interval_modify(struct nb_cb_modify_args *args);
+int ripd_instance_timers_holddown_interval_modify(
+ struct nb_cb_modify_args *args);
+int ripd_instance_timers_update_interval_modify(struct nb_cb_modify_args *args);
+int ripd_instance_version_receive_modify(struct nb_cb_modify_args *args);
+int ripd_instance_version_send_modify(struct nb_cb_modify_args *args);
+int ripd_instance_default_bfd_profile_modify(struct nb_cb_modify_args *args);
+int ripd_instance_default_bfd_profile_destroy(struct nb_cb_destroy_args *args);
+const void *ripd_instance_state_neighbors_neighbor_get_next(
+ struct nb_cb_get_next_args *args);
+int ripd_instance_state_neighbors_neighbor_get_keys(
+ struct nb_cb_get_keys_args *args);
+const void *ripd_instance_state_neighbors_neighbor_lookup_entry(
+ struct nb_cb_lookup_entry_args *args);
+struct yang_data *ripd_instance_state_neighbors_neighbor_address_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *ripd_instance_state_neighbors_neighbor_last_update_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *
+ripd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *
+ripd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem(
+ struct nb_cb_get_elem_args *args);
+const void *
+ripd_instance_state_routes_route_get_next(struct nb_cb_get_next_args *args);
+int ripd_instance_state_routes_route_get_keys(struct nb_cb_get_keys_args *args);
+const void *ripd_instance_state_routes_route_lookup_entry(
+ struct nb_cb_lookup_entry_args *args);
+struct yang_data *ripd_instance_state_routes_route_prefix_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *ripd_instance_state_routes_route_next_hop_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *ripd_instance_state_routes_route_interface_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *ripd_instance_state_routes_route_metric_get_elem(
+ struct nb_cb_get_elem_args *args);
+const void *ripd_instance_state_routes_route_nexthops_nexthop_get_next(
+ struct nb_cb_get_next_args *args);
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_nh_type_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_protocol_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_rip_type_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_gateway_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_interface_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_from_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_tag_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_external_metric_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_expire_time_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *ripd_instance_state_routes_route_metric_get_elem(
+ struct nb_cb_get_elem_args *args);
+int clear_rip_route_rpc(struct nb_cb_rpc_args *args);
+int lib_interface_rip_split_horizon_modify(struct nb_cb_modify_args *args);
+int lib_interface_rip_v2_broadcast_modify(struct nb_cb_modify_args *args);
+int lib_interface_rip_version_receive_modify(struct nb_cb_modify_args *args);
+int lib_interface_rip_version_send_modify(struct nb_cb_modify_args *args);
+int lib_interface_rip_authentication_scheme_mode_modify(
+ struct nb_cb_modify_args *args);
+int lib_interface_rip_authentication_scheme_md5_auth_length_modify(
+ struct nb_cb_modify_args *args);
+int lib_interface_rip_authentication_scheme_md5_auth_length_destroy(
+ struct nb_cb_destroy_args *args);
+int lib_interface_rip_authentication_password_modify(
+ struct nb_cb_modify_args *args);
+int lib_interface_rip_authentication_password_destroy(
+ struct nb_cb_destroy_args *args);
+int lib_interface_rip_authentication_key_chain_modify(
+ struct nb_cb_modify_args *args);
+int lib_interface_rip_authentication_key_chain_destroy(
+ struct nb_cb_destroy_args *args);
+int lib_interface_rip_bfd_create(struct nb_cb_create_args *args);
+int lib_interface_rip_bfd_destroy(struct nb_cb_destroy_args *args);
+int lib_interface_rip_bfd_enable_modify(struct nb_cb_modify_args *args);
+int lib_interface_rip_bfd_enable_destroy(struct nb_cb_destroy_args *args);
+int lib_interface_rip_bfd_profile_modify(struct nb_cb_modify_args *args);
+int lib_interface_rip_bfd_profile_destroy(struct nb_cb_destroy_args *args);
+
+/* Optional 'apply_finish' callbacks. */
+void ripd_instance_redistribute_apply_finish(
+ struct nb_cb_apply_finish_args *args);
+void ripd_instance_timers_apply_finish(struct nb_cb_apply_finish_args *args);
+
+/* Optional 'cli_show' callbacks. */
+void cli_show_router_rip(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_allow_ecmp(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_default_information_originate(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_default_metric(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_distance(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_distance_source(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_neighbor(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_network_prefix(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_network_interface(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_offset_list(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_passive_default(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_passive_interface(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_non_passive_interface(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_redistribute(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_route(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_timers(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_rip_version(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_rip_split_horizon(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_rip_v2_broadcast(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_rip_receive_version(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_rip_send_version(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripd_instance_default_bfd_profile(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_rip_authentication_scheme(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_rip_authentication_string(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_rip_authentication_key_chain(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_rip_bfd_enable(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_rip_bfd_profile(struct vty *vty, const 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 0000000..8d3b670
--- /dev/null
+++ b/ripd/rip_nb_config.c
@@ -0,0 +1,1246 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ * Copyright (C) 2023 LabN Consulting, L.L.C.
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "vrf.h"
+#include "log.h"
+#include "prefix.h"
+#include "table.h"
+#include "command.h"
+#include "if_rmap.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"
+#include "ripd/rip_bfd.h"
+
+/*
+ * XPath: /frr-ripd:ripd/instance
+ */
+int ripd_instance_create(struct nb_cb_create_args *args)
+{
+ struct rip *rip;
+ struct vrf *vrf;
+ const char *vrf_name;
+ int socket;
+
+ vrf_name = yang_dnode_get_string(args->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 (args->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;
+ args->resource->fd = socket;
+ break;
+ case NB_EV_ABORT:
+ if (!vrf || !vrf_is_enabled(vrf))
+ break;
+
+ socket = args->resource->fd;
+ close(socket);
+ break;
+ case NB_EV_APPLY:
+ if (vrf && vrf_is_enabled(vrf))
+ socket = args->resource->fd;
+ else
+ socket = -1;
+
+ rip = rip_create(vrf_name, vrf, socket);
+ nb_running_set_entry(args->dnode, rip);
+ break;
+ }
+
+ return NB_OK;
+}
+
+int ripd_instance_destroy(struct nb_cb_destroy_args *args)
+{
+ struct rip *rip;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_unset_entry(args->dnode);
+ rip_clean(rip);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/allow-ecmp
+ */
+int ripd_instance_allow_ecmp_modify(struct nb_cb_modify_args *args)
+{
+ struct rip *rip;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ rip->ecmp =
+ MIN(yang_dnode_get_uint8(args->dnode, NULL), zebra_ecmp_count);
+ if (!rip->ecmp) {
+ rip_ecmp_disable(rip);
+ return NB_OK;
+ }
+
+ rip_ecmp_change(rip);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/default-information-originate
+ */
+int ripd_instance_default_information_originate_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct rip *rip;
+ bool default_information;
+ struct prefix_ipv4 p;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ default_information = yang_dnode_get_bool(args->dnode, NULL);
+
+ memset(&p, 0, sizeof(p));
+ 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(struct nb_cb_modify_args *args)
+{
+ struct rip *rip;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ rip->default_metric = yang_dnode_get_uint8(args->dnode, NULL);
+ /* rip_update_default_metric (); */
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/distance/default
+ */
+int ripd_instance_distance_default_modify(struct nb_cb_modify_args *args)
+{
+ struct rip *rip;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ rip->distance = yang_dnode_get_uint8(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/distance/source
+ */
+int ripd_instance_distance_source_create(struct nb_cb_create_args *args)
+{
+ struct rip *rip;
+ struct prefix_ipv4 prefix;
+ struct route_node *rn;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ yang_dnode_get_ipv4p(&prefix, args->dnode, "./prefix");
+ apply_mask_ipv4(&prefix);
+
+ /* Get RIP distance node. */
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ rn = route_node_get(rip->distance_table, (struct prefix *)&prefix);
+ rn->info = rip_distance_new();
+ nb_running_set_entry(args->dnode, rn);
+
+ return NB_OK;
+}
+
+int ripd_instance_distance_source_destroy(struct nb_cb_destroy_args *args)
+{
+ struct route_node *rn;
+ struct rip_distance *rdistance;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rn = nb_running_unset_entry(args->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(
+ struct nb_cb_modify_args *args)
+{
+ struct route_node *rn;
+ uint8_t distance;
+ struct rip_distance *rdistance;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ /* Set distance value. */
+ rn = nb_running_get_entry(args->dnode, NULL, true);
+ distance = yang_dnode_get_uint8(args->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(
+ struct nb_cb_modify_args *args)
+{
+ const char *acl_name;
+ struct route_node *rn;
+ struct rip_distance *rdistance;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ acl_name = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set access-list */
+ rn = nb_running_get_entry(args->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(
+ struct nb_cb_destroy_args *args)
+{
+ struct route_node *rn;
+ struct rip_distance *rdistance;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ /* Reset access-list configuration. */
+ rn = nb_running_get_entry(args->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(struct nb_cb_create_args *args)
+{
+ struct rip *rip;
+ struct prefix_ipv4 p;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_BITLEN;
+ yang_dnode_get_ipv4(&p.prefix, args->dnode, NULL);
+
+ return rip_neighbor_add(rip, &p);
+}
+
+int ripd_instance_explicit_neighbor_destroy(struct nb_cb_destroy_args *args)
+{
+ struct rip *rip;
+ struct prefix_ipv4 p;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_BITLEN;
+ yang_dnode_get_ipv4(&p.prefix, args->dnode, NULL);
+
+ return rip_neighbor_delete(rip, &p);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/network
+ */
+int ripd_instance_network_create(struct nb_cb_create_args *args)
+{
+ struct rip *rip;
+ struct prefix p;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ yang_dnode_get_ipv4p(&p, args->dnode, NULL);
+ apply_mask_ipv4((struct prefix_ipv4 *)&p);
+
+ return rip_enable_network_add(rip, &p);
+}
+
+int ripd_instance_network_destroy(struct nb_cb_destroy_args *args)
+{
+ struct rip *rip;
+ struct prefix p;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ yang_dnode_get_ipv4p(&p, args->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(struct nb_cb_create_args *args)
+{
+ struct rip *rip;
+ const char *ifname;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ ifname = yang_dnode_get_string(args->dnode, NULL);
+
+ return rip_enable_if_add(rip, ifname);
+}
+
+int ripd_instance_interface_destroy(struct nb_cb_destroy_args *args)
+{
+ struct rip *rip;
+ const char *ifname;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ ifname = yang_dnode_get_string(args->dnode, NULL);
+
+ return rip_enable_if_delete(rip, ifname);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/offset-list
+ */
+int ripd_instance_offset_list_create(struct nb_cb_create_args *args)
+{
+ struct rip *rip;
+ const char *ifname;
+ struct rip_offset_list *offset;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ ifname = yang_dnode_get_string(args->dnode, "./interface");
+
+ offset = rip_offset_list_new(rip, ifname);
+ nb_running_set_entry(args->dnode, offset);
+
+ return NB_OK;
+}
+
+int ripd_instance_offset_list_destroy(struct nb_cb_destroy_args *args)
+{
+ int direct;
+ struct rip_offset_list *offset;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ direct = yang_dnode_get_enum(args->dnode, "./direction");
+
+ offset = nb_running_unset_entry(args->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(struct nb_cb_modify_args *args)
+{
+ int direct;
+ struct rip_offset_list *offset;
+ const char *alist_name;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ direct = yang_dnode_get_enum(args->dnode, "../direction");
+ alist_name = yang_dnode_get_string(args->dnode, NULL);
+
+ offset = nb_running_get_entry(args->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(struct nb_cb_modify_args *args)
+{
+ int direct;
+ uint8_t metric;
+ struct rip_offset_list *offset;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ direct = yang_dnode_get_enum(args->dnode, "../direction");
+ metric = yang_dnode_get_uint8(args->dnode, NULL);
+
+ offset = nb_running_get_entry(args->dnode, NULL, true);
+ offset->direct[direct].metric = metric;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/passive-default
+ */
+int ripd_instance_passive_default_modify(struct nb_cb_modify_args *args)
+{
+ struct rip *rip;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ rip->passive_default = yang_dnode_get_bool(args->dnode, NULL);
+ rip_passive_nondefault_clean(rip);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/passive-interface
+ */
+int ripd_instance_passive_interface_create(struct nb_cb_create_args *args)
+{
+ struct rip *rip;
+ const char *ifname;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ ifname = yang_dnode_get_string(args->dnode, NULL);
+
+ return rip_passive_nondefault_set(rip, ifname);
+}
+
+int ripd_instance_passive_interface_destroy(struct nb_cb_destroy_args *args)
+{
+ struct rip *rip;
+ const char *ifname;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ ifname = yang_dnode_get_string(args->dnode, NULL);
+
+ return rip_passive_nondefault_unset(rip, ifname);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/non-passive-interface
+ */
+int ripd_instance_non_passive_interface_create(struct nb_cb_create_args *args)
+{
+ struct rip *rip;
+ const char *ifname;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ ifname = yang_dnode_get_string(args->dnode, NULL);
+
+ return rip_passive_nondefault_set(rip, ifname);
+}
+
+int ripd_instance_non_passive_interface_destroy(struct nb_cb_destroy_args *args)
+{
+ struct rip *rip;
+ const char *ifname;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ ifname = yang_dnode_get_string(args->dnode, NULL);
+
+ return rip_passive_nondefault_unset(rip, ifname);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/redistribute
+ */
+int ripd_instance_redistribute_create(struct nb_cb_create_args *args)
+{
+ struct rip *rip;
+ int type;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_enum(args->dnode, "./protocol");
+
+ rip->redist[type].enabled = true;
+
+ return NB_OK;
+}
+
+int ripd_instance_redistribute_destroy(struct nb_cb_destroy_args *args)
+{
+ struct rip *rip;
+ int type;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_enum(args->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(
+ struct nb_cb_apply_finish_args *args)
+{
+ struct rip *rip;
+ int type;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_enum(args->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(struct nb_cb_modify_args *args)
+{
+ struct rip *rip;
+ int type;
+ const char *rmap_name;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_enum(args->dnode, "../protocol");
+ rmap_name = yang_dnode_get_string(args->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(
+ struct nb_cb_destroy_args *args)
+{
+ struct rip *rip;
+ int type;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_enum(args->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(struct nb_cb_modify_args *args)
+{
+ struct rip *rip;
+ int type;
+ uint8_t metric;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_enum(args->dnode, "../protocol");
+ metric = yang_dnode_get_uint8(args->dnode, NULL);
+
+ rip->redist[type].metric_config = true;
+ rip->redist[type].metric = metric;
+
+ return NB_OK;
+}
+
+int ripd_instance_redistribute_metric_destroy(struct nb_cb_destroy_args *args)
+{
+ struct rip *rip;
+ int type;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_enum(args->dnode, "../protocol");
+
+ rip->redist[type].metric_config = false;
+ rip->redist[type].metric = 0;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/if-route-maps/if-route-map
+ */
+int ripd_instance_if_route_maps_if_route_map_create(
+ struct nb_cb_create_args *args)
+{
+ /* if_rmap is created when first routemap is added */
+ return NB_OK;
+}
+
+int ripd_instance_if_route_maps_if_route_map_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct rip *rip;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ /*
+ * YANG will prune edit deletes up to the most general deleted node so
+ * we need to handle deleting any existing state underneath and not
+ * count on those more specific callbacks being called individually.
+ */
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ if_rmap_yang_destroy_cb(rip->if_rmap_ctx, args->dnode);
+
+ return NB_OK;
+}
+
+static void if_route_map_modify(const struct lyd_node *dnode,
+ enum if_rmap_type type, bool delete)
+{
+ struct rip *rip = nb_running_get_entry(dnode, NULL, true);
+
+ if_rmap_yang_modify_cb(rip->if_rmap_ctx, dnode, type, delete);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/if-route-maps/if-route-map/in-route-map
+ */
+int ripd_instance_if_route_maps_if_route_map_in_route_map_modify(
+ struct nb_cb_modify_args *args)
+{
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ if_route_map_modify(args->dnode, IF_RMAP_IN, false);
+
+ return NB_OK;
+}
+
+int ripd_instance_if_route_maps_if_route_map_in_route_map_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ if_route_map_modify(args->dnode, IF_RMAP_IN, true);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/if-route-maps/if-route-map/out-route-map
+ */
+int ripd_instance_if_route_maps_if_route_map_out_route_map_modify(
+ struct nb_cb_modify_args *args)
+{
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ if_route_map_modify(args->dnode, IF_RMAP_OUT, false);
+
+ return NB_OK;
+}
+
+int ripd_instance_if_route_maps_if_route_map_out_route_map_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ if_route_map_modify(args->dnode, IF_RMAP_OUT, true);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/static-route
+ */
+int ripd_instance_static_route_create(struct nb_cb_create_args *args)
+{
+ struct rip *rip;
+ struct nexthop nh;
+ struct prefix_ipv4 p;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ yang_dnode_get_ipv4p(&p, args->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(struct nb_cb_destroy_args *args)
+{
+ struct rip *rip;
+ struct prefix_ipv4 p;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ yang_dnode_get_ipv4p(&p, args->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(struct nb_cb_apply_finish_args *args)
+{
+ struct rip *rip;
+
+ rip = nb_running_get_entry(args->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(struct nb_cb_modify_args *args)
+{
+ struct rip *rip;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ rip->garbage_time = yang_dnode_get_uint32(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/timers/holddown-interval
+ */
+int ripd_instance_timers_holddown_interval_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct rip *rip;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ rip->timeout_time = yang_dnode_get_uint32(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/timers/update-interval
+ */
+int ripd_instance_timers_update_interval_modify(struct nb_cb_modify_args *args)
+{
+ struct rip *rip;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ rip->update_time = yang_dnode_get_uint32(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/version/receive
+ */
+int ripd_instance_version_receive_modify(struct nb_cb_modify_args *args)
+{
+ struct rip *rip;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ rip->version_recv = yang_dnode_get_enum(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/version/send
+ */
+int ripd_instance_version_send_modify(struct nb_cb_modify_args *args)
+{
+ struct rip *rip;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ rip->version_send = yang_dnode_get_enum(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/default-bfd-profile
+ */
+int ripd_instance_default_bfd_profile_modify(struct nb_cb_modify_args *args)
+{
+ struct rip *rip;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ XFREE(MTYPE_RIP_BFD_PROFILE, rip->default_bfd_profile);
+ rip->default_bfd_profile =
+ XSTRDUP(MTYPE_RIP_BFD_PROFILE,
+ yang_dnode_get_string(args->dnode, NULL));
+ rip_bfd_instance_update(rip);
+
+ return NB_OK;
+}
+
+int ripd_instance_default_bfd_profile_destroy(struct nb_cb_destroy_args *args)
+{
+ struct rip *rip;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ XFREE(MTYPE_RIP_BFD_PROFILE, rip->default_bfd_profile);
+ rip_bfd_instance_update(rip);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/split-horizon
+ */
+int lib_interface_rip_split_horizon_modify(struct nb_cb_modify_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ ri->split_horizon = yang_dnode_get_enum(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/v2-broadcast
+ */
+int lib_interface_rip_v2_broadcast_modify(struct nb_cb_modify_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ ri->v2_broadcast = yang_dnode_get_bool(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/version-receive
+ */
+int lib_interface_rip_version_receive_modify(struct nb_cb_modify_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ ri->ri_receive = yang_dnode_get_enum(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/version-send
+ */
+int lib_interface_rip_version_send_modify(struct nb_cb_modify_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ ri->ri_send = yang_dnode_get_enum(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-scheme/mode
+ */
+int lib_interface_rip_authentication_scheme_mode_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ ri->auth_type = yang_dnode_get_enum(args->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(
+ struct nb_cb_modify_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ ri->md5_auth_len = yang_dnode_get_enum(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+int lib_interface_rip_authentication_scheme_md5_auth_length_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->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(
+ struct nb_cb_modify_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->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(args->dnode, NULL));
+
+ return NB_OK;
+}
+
+int lib_interface_rip_authentication_password_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->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/bfd-monitoring
+ */
+int lib_interface_rip_bfd_create(struct nb_cb_create_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ ri->bfd.enabled = yang_dnode_get_bool(args->dnode, "./enable");
+ XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile);
+ if (yang_dnode_exists(args->dnode, "./profile"))
+ ri->bfd.profile = XSTRDUP(
+ MTYPE_RIP_BFD_PROFILE,
+ yang_dnode_get_string(args->dnode, "./profile"));
+
+ rip_bfd_interface_update(ri);
+
+ return NB_OK;
+}
+
+int lib_interface_rip_bfd_destroy(struct nb_cb_destroy_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ ri->bfd.enabled = false;
+ XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile);
+ rip_bfd_interface_update(ri);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/enable
+ */
+int lib_interface_rip_bfd_enable_modify(struct nb_cb_modify_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ ri->bfd.enabled = yang_dnode_get_bool(args->dnode, NULL);
+ rip_bfd_interface_update(ri);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/profile
+ */
+int lib_interface_rip_bfd_profile_modify(struct nb_cb_modify_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile);
+ ri->bfd.profile = XSTRDUP(MTYPE_RIP_BFD_PROFILE,
+ yang_dnode_get_string(args->dnode, NULL));
+ rip_bfd_interface_update(ri);
+
+ return NB_OK;
+}
+
+int lib_interface_rip_bfd_profile_destroy(struct nb_cb_destroy_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile);
+ rip_bfd_interface_update(ri);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-key-chain
+ */
+int lib_interface_rip_authentication_key_chain_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->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(args->dnode, NULL));
+
+ return NB_OK;
+}
+
+int lib_interface_rip_authentication_key_chain_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->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 0000000..80da39b
--- /dev/null
+++ b/ripd/rip_nb_notifications.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ */
+
+#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 0000000..bbe3d0f
--- /dev/null
+++ b/ripd/rip_nb_rpcs.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ */
+
+#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) {
+ EVENT_OFF(rinfo->t_timeout);
+ EVENT_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(struct nb_cb_rpc_args *args)
+{
+ struct rip *rip;
+ struct yang_data *yang_vrf;
+
+ yang_vrf = yang_data_list_find(args->input, "%s/%s", args->xpath,
+ "input/vrf");
+ if (yang_vrf) {
+ rip = rip_lookup_by_vrf_name(yang_vrf->value);
+ if (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 0000000..fa0d382
--- /dev/null
+++ b/ripd/rip_nb_state.c
@@ -0,0 +1,433 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ */
+
+#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(struct nb_cb_get_next_args *args)
+{
+ struct rip *rip = (struct rip *)args->list_entry;
+
+ if (args->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(struct nb_cb_get_keys_args *args)
+{
+ const struct rip *rip = args->list_entry;
+
+ args->keys->num = 1;
+ strlcpy(args->keys->key[0], rip->vrf_name, sizeof(args->keys->key[0]));
+
+ return NB_OK;
+}
+
+const void *ripd_instance_lookup_entry(struct nb_cb_lookup_entry_args *args)
+{
+ const char *vrf_name = args->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(
+ struct nb_cb_get_next_args *args)
+{
+ const struct rip *rip = args->parent_list_entry;
+ struct listnode *node;
+
+ if (args->list_entry == NULL)
+ node = listhead(rip->peer_list);
+ else
+ node = listnextnode((struct listnode *)args->list_entry);
+
+ return node;
+}
+
+int ripd_instance_state_neighbors_neighbor_get_keys(
+ struct nb_cb_get_keys_args *args)
+{
+ const struct listnode *node = args->list_entry;
+ const struct rip_peer *peer = listgetdata(node);
+
+ args->keys->num = 1;
+ (void)inet_ntop(AF_INET, &peer->addr, args->keys->key[0],
+ sizeof(args->keys->key[0]));
+
+ return NB_OK;
+}
+
+const void *ripd_instance_state_neighbors_neighbor_lookup_entry(
+ struct nb_cb_lookup_entry_args *args)
+{
+ const struct rip *rip = args->parent_list_entry;
+ struct in_addr address;
+ struct rip_peer *peer;
+ struct listnode *node;
+
+ yang_str2ipv4(args->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(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct listnode *node = args->list_entry;
+ const struct rip_peer *peer = listgetdata(node);
+
+ return yang_data_new_ipv4(args->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(
+ struct nb_cb_get_elem_args *args)
+{
+ /* 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(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct listnode *node = args->list_entry;
+ const struct rip_peer *peer = listgetdata(node);
+
+ return yang_data_new_uint32(args->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(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct listnode *node = args->list_entry;
+ const struct rip_peer *peer = listgetdata(node);
+
+ return yang_data_new_uint32(args->xpath, peer->recv_badroutes);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route
+ */
+const void *
+ripd_instance_state_routes_route_get_next(struct nb_cb_get_next_args *args)
+{
+ const struct rip *rip = args->parent_list_entry;
+ struct route_node *rn;
+
+ if (args->list_entry == NULL)
+ rn = route_top(rip->table);
+ else
+ rn = route_next((struct route_node *)args->list_entry);
+ /* Optimization: skip empty route nodes. */
+ while (rn && rn->info == NULL)
+ rn = route_next(rn);
+
+ return rn;
+}
+
+int ripd_instance_state_routes_route_get_keys(struct nb_cb_get_keys_args *args)
+{
+ const struct route_node *rn = args->list_entry;
+
+ args->keys->num = 1;
+ (void)prefix2str(&rn->p, args->keys->key[0],
+ sizeof(args->keys->key[0]));
+
+ return NB_OK;
+}
+
+const void *ripd_instance_state_routes_route_lookup_entry(
+ struct nb_cb_lookup_entry_args *args)
+{
+ const struct rip *rip = args->parent_list_entry;
+ struct prefix prefix;
+ struct route_node *rn;
+
+ yang_str2ipv4p(args->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(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct route_node *rn = args->list_entry;
+ const struct rip_info *rinfo = listnode_head(rn->info);
+
+ assert(rinfo);
+ return yang_data_new_ipv4p(args->xpath, &rinfo->rp->p);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop
+ */
+const void *ripd_instance_state_routes_route_nexthops_nexthop_get_next(
+ struct nb_cb_get_next_args *args)
+{
+ const struct route_node *rn = args->parent_list_entry;
+ const struct listnode *node = args->list_entry;
+
+ assert(rn);
+ if (node)
+ return listnextnode(node);
+ assert(rn->info);
+ return listhead((struct list *)rn->info);
+}
+
+static inline const struct rip_info *get_rip_info(const void *info)
+{
+ return (const struct rip_info *)listgetdata(
+ (const struct listnode *)info);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/nh-type
+ */
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_nh_type_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct rip_info *rinfo = get_rip_info(args->list_entry);
+
+ assert(rinfo);
+ return yang_data_new_enum(args->xpath, rinfo->nh.type);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/protocol
+ */
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_protocol_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct rip_info *rinfo = get_rip_info(args->list_entry);
+
+ assert(rinfo);
+ return yang_data_new_enum(args->xpath, rinfo->type);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/rip-type
+ */
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_rip_type_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct rip_info *rinfo = get_rip_info(args->list_entry);
+
+ assert(rinfo);
+ return yang_data_new_enum(args->xpath, rinfo->sub_type);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/gateway
+ */
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_gateway_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct rip_info *rinfo = get_rip_info(args->list_entry);
+
+ if (rinfo->nh.type != NEXTHOP_TYPE_IPV4 &&
+ rinfo->nh.type != NEXTHOP_TYPE_IPV4_IFINDEX)
+ return NULL;
+
+ return yang_data_new_ipv4(args->xpath, &rinfo->nh.gate.ipv4);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/interface
+ */
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_interface_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct rip_info *rinfo = get_rip_info(args->list_entry);
+ const struct rip *rip = rip_info_get_instance(rinfo);
+
+ if (rinfo->nh.type != NEXTHOP_TYPE_IFINDEX &&
+ rinfo->nh.type != NEXTHOP_TYPE_IPV4_IFINDEX)
+ return NULL;
+
+ return yang_data_new_string(
+ args->xpath,
+ ifindex2ifname(rinfo->nh.ifindex, rip->vrf->vrf_id));
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/from
+ */
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_from_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct rip_info *rinfo = get_rip_info(args->list_entry);
+
+ if (rinfo->type != ZEBRA_ROUTE_RIP || rinfo->sub_type != RIP_ROUTE_RTE)
+ return NULL;
+
+ return yang_data_new_ipv4(args->xpath, &rinfo->from);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/tag
+ */
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_tag_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct rip_info *rinfo = get_rip_info(args->list_entry);
+
+ return yang_data_new_uint32(args->xpath, rinfo->tag);
+}
+
+/*
+ * XPath:
+ * /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/external-metric
+ */
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_external_metric_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct rip_info *rinfo = get_rip_info(args->list_entry);
+
+ if ((rinfo->type == ZEBRA_ROUTE_RIP &&
+ rinfo->sub_type == RIP_ROUTE_RTE) ||
+ rinfo->metric == RIP_METRIC_INFINITY || rinfo->external_metric == 0)
+ return NULL;
+ return yang_data_new_uint32(args->xpath, rinfo->external_metric);
+}
+
+/*
+ * XPath:
+ * /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/expire-time
+ */
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_expire_time_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct rip_info *rinfo = get_rip_info(args->list_entry);
+ struct event *event;
+
+ if ((event = rinfo->t_timeout) == NULL)
+ event = rinfo->t_garbage_collect;
+ if (!event)
+ return NULL;
+
+ return yang_data_new_uint32(args->xpath,
+ event_timer_remain_second(event));
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/next-hop
+ */
+struct yang_data *ripd_instance_state_routes_route_next_hop_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct route_node *rn = args->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(args->xpath, &rinfo->nh.gate.ipv4);
+ case NEXTHOP_TYPE_IFINDEX:
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ case NEXTHOP_TYPE_BLACKHOLE:
+ return NULL;
+ }
+
+ assert(!"Reached end of function where we do not expect to reach");
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/interface
+ */
+struct yang_data *ripd_instance_state_routes_route_interface_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct route_node *rn = args->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(
+ args->xpath,
+ ifindex2ifname(rinfo->nh.ifindex, rip->vrf->vrf_id));
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ case NEXTHOP_TYPE_BLACKHOLE:
+ return NULL;
+ }
+
+ assert(!"Reached end of function where we do not expect to reach");
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/metric
+ */
+struct yang_data *ripd_instance_state_routes_route_metric_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct route_node *rn = args->list_entry;
+ const struct rip_info *rinfo = listnode_head(rn->info);
+
+ return yang_data_new_uint8(args->xpath, rinfo->metric);
+}
diff --git a/ripd/rip_offset.c b/ripd/rip_offset.c
new file mode 100644
index 0000000..4c93f71
--- /dev/null
+++ b/ripd/rip_offset.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RIP offset-list
+ * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "prefix.h"
+#include "filter.h"
+#include "command.h"
+#include "linklist.h"
+#include "memory.h"
+
+#include "ripd/ripd.h"
+
+DEFINE_MTYPE_STATIC(RIPD, RIP_OFFSET_LIST, "RIP offset list");
+
+#define OFFSET_LIST_IN_NAME(O) ((O)->direct[RIP_OFFSET_LIST_IN].alist_name)
+#define OFFSET_LIST_IN_METRIC(O) ((O)->direct[RIP_OFFSET_LIST_IN].metric)
+
+#define OFFSET_LIST_OUT_NAME(O) ((O)->direct[RIP_OFFSET_LIST_OUT].alist_name)
+#define OFFSET_LIST_OUT_METRIC(O) ((O)->direct[RIP_OFFSET_LIST_OUT].metric)
+
+struct rip_offset_list *rip_offset_list_new(struct rip *rip, const char *ifname)
+{
+ struct rip_offset_list *offset;
+
+ offset = XCALLOC(MTYPE_RIP_OFFSET_LIST, sizeof(struct rip_offset_list));
+ offset->rip = rip;
+ offset->ifname = strdup(ifname);
+ listnode_add_sort(rip->offset_list_master, offset);
+
+ return offset;
+}
+
+void offset_list_del(struct rip_offset_list *offset)
+{
+ listnode_delete(offset->rip->offset_list_master, offset);
+ offset_list_free(offset);
+}
+
+void offset_list_free(struct rip_offset_list *offset)
+{
+ if (OFFSET_LIST_IN_NAME(offset))
+ free(OFFSET_LIST_IN_NAME(offset));
+ if (OFFSET_LIST_OUT_NAME(offset))
+ free(OFFSET_LIST_OUT_NAME(offset));
+ free(offset->ifname);
+ XFREE(MTYPE_RIP_OFFSET_LIST, offset);
+}
+
+struct rip_offset_list *rip_offset_list_lookup(struct rip *rip,
+ const char *ifname)
+{
+ struct rip_offset_list *offset;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(rip->offset_list_master, node, nnode, offset)) {
+ if (strcmp(offset->ifname, ifname) == 0)
+ return offset;
+ }
+ return NULL;
+}
+
+/* If metric is modified return 1. */
+int rip_offset_list_apply_in(struct prefix_ipv4 *p, struct interface *ifp,
+ uint32_t *metric)
+{
+ struct rip_interface *ri = ifp->info;
+ struct rip_offset_list *offset;
+ struct access_list *alist;
+
+ /* Look up offset-list with interface name. */
+ offset = rip_offset_list_lookup(ri->rip, ifp->name);
+ if (offset && OFFSET_LIST_IN_NAME(offset)) {
+ alist = access_list_lookup(AFI_IP, OFFSET_LIST_IN_NAME(offset));
+
+ if (alist
+ && access_list_apply(alist, (struct prefix *)p)
+ == FILTER_PERMIT) {
+ *metric += OFFSET_LIST_IN_METRIC(offset);
+ return 1;
+ }
+ return 0;
+ }
+ /* Look up offset-list without interface name. */
+ offset = rip_offset_list_lookup(ri->rip, "*");
+ if (offset && OFFSET_LIST_IN_NAME(offset)) {
+ alist = access_list_lookup(AFI_IP, OFFSET_LIST_IN_NAME(offset));
+
+ if (alist
+ && access_list_apply(alist, (struct prefix *)p)
+ == FILTER_PERMIT) {
+ *metric += OFFSET_LIST_IN_METRIC(offset);
+ return 1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+/* If metric is modified return 1. */
+int rip_offset_list_apply_out(struct prefix_ipv4 *p, struct interface *ifp,
+ uint32_t *metric)
+{
+ struct rip_interface *ri = ifp->info;
+ struct rip_offset_list *offset;
+ struct access_list *alist;
+
+ /* Look up offset-list with interface name. */
+ offset = rip_offset_list_lookup(ri->rip, ifp->name);
+ if (offset && OFFSET_LIST_OUT_NAME(offset)) {
+ alist = access_list_lookup(AFI_IP,
+ OFFSET_LIST_OUT_NAME(offset));
+
+ if (alist
+ && access_list_apply(alist, (struct prefix *)p)
+ == FILTER_PERMIT) {
+ *metric += OFFSET_LIST_OUT_METRIC(offset);
+ return 1;
+ }
+ return 0;
+ }
+
+ /* Look up offset-list without interface name. */
+ offset = rip_offset_list_lookup(ri->rip, "*");
+ if (offset && OFFSET_LIST_OUT_NAME(offset)) {
+ alist = access_list_lookup(AFI_IP,
+ OFFSET_LIST_OUT_NAME(offset));
+
+ if (alist
+ && access_list_apply(alist, (struct prefix *)p)
+ == FILTER_PERMIT) {
+ *metric += OFFSET_LIST_OUT_METRIC(offset);
+ return 1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+int offset_list_cmp(struct rip_offset_list *o1, struct rip_offset_list *o2)
+{
+ return strcmp(o1->ifname, o2->ifname);
+}
diff --git a/ripd/rip_peer.c b/ripd/rip_peer.c
new file mode 100644
index 0000000..3e8ddec
--- /dev/null
+++ b/ripd/rip_peer.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RIP peer support
+ * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "prefix.h"
+#include "command.h"
+#include "linklist.h"
+#include "frrevent.h"
+#include "memory.h"
+#include "table.h"
+
+#include "ripd/ripd.h"
+#include "ripd/rip_bfd.h"
+
+DEFINE_MTYPE_STATIC(RIPD, RIP_PEER, "RIP peer");
+
+static struct rip_peer *rip_peer_new(void)
+{
+ return XCALLOC(MTYPE_RIP_PEER, sizeof(struct rip_peer));
+}
+
+void rip_peer_free(struct rip_peer *peer)
+{
+ bfd_sess_free(&peer->bfd_session);
+ EVENT_OFF(peer->t_timeout);
+ XFREE(MTYPE_RIP_PEER, peer);
+}
+
+struct rip_peer *rip_peer_lookup(struct rip *rip, struct in_addr *addr)
+{
+ struct rip_peer *peer;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(rip->peer_list, node, nnode, peer)) {
+ if (IPV4_ADDR_SAME(&peer->addr, addr))
+ return peer;
+ }
+ return NULL;
+}
+
+struct rip_peer *rip_peer_lookup_next(struct rip *rip, struct in_addr *addr)
+{
+ struct rip_peer *peer;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(rip->peer_list, node, nnode, peer)) {
+ if (htonl(peer->addr.s_addr) > htonl(addr->s_addr))
+ return peer;
+ }
+ return NULL;
+}
+
+/* RIP peer is timeout. */
+static void rip_peer_timeout(struct event *t)
+{
+ struct rip_peer *peer;
+
+ peer = EVENT_ARG(t);
+ listnode_delete(peer->rip->peer_list, peer);
+ rip_peer_free(peer);
+}
+
+/* Get RIP peer. At the same time update timeout thread. */
+static struct rip_peer *rip_peer_get(struct rip *rip, struct rip_interface *ri,
+ struct in_addr *addr)
+{
+ struct rip_peer *peer;
+
+ peer = rip_peer_lookup(rip, addr);
+
+ if (peer) {
+ EVENT_OFF(peer->t_timeout);
+ } else {
+ peer = rip_peer_new();
+ peer->rip = rip;
+ peer->ri = ri;
+ peer->addr = *addr;
+ rip_bfd_session_update(peer);
+ listnode_add_sort(rip->peer_list, peer);
+ }
+
+ /* Update timeout thread. */
+ event_add_timer(master, rip_peer_timeout, peer, RIP_PEER_TIMER_DEFAULT,
+ &peer->t_timeout);
+
+ /* Last update time set. */
+ time(&peer->uptime);
+
+ return peer;
+}
+
+void rip_peer_update(struct rip *rip, struct rip_interface *ri,
+ struct sockaddr_in *from, uint8_t version)
+{
+ struct rip_peer *peer;
+ peer = rip_peer_get(rip, ri, &from->sin_addr);
+ peer->version = version;
+}
+
+void rip_peer_bad_route(struct rip *rip, struct rip_interface *ri,
+ struct sockaddr_in *from)
+{
+ struct rip_peer *peer;
+ peer = rip_peer_get(rip, ri, &from->sin_addr);
+ peer->recv_badroutes++;
+}
+
+void rip_peer_bad_packet(struct rip *rip, struct rip_interface *ri,
+ struct sockaddr_in *from)
+{
+ struct rip_peer *peer;
+ peer = rip_peer_get(rip, ri, &from->sin_addr);
+ peer->recv_badpackets++;
+}
+
+/* Display peer uptime. */
+static char *rip_peer_uptime(struct rip_peer *peer, char *buf, size_t len)
+{
+ time_t uptime;
+
+ /* If there is no connection has been done before print `never'. */
+ if (peer->uptime == 0) {
+ snprintf(buf, len, "never ");
+ return buf;
+ }
+
+ /* Get current time. */
+ uptime = time(NULL);
+ uptime -= peer->uptime;
+
+ frrtime_to_interval(uptime, buf, len);
+
+ return buf;
+}
+
+void rip_peer_display(struct vty *vty, struct rip *rip)
+{
+ struct rip_peer *peer;
+ struct listnode *node, *nnode;
+#define RIP_UPTIME_LEN 25
+ char timebuf[RIP_UPTIME_LEN];
+
+ for (ALL_LIST_ELEMENTS(rip->peer_list, node, nnode, peer)) {
+ vty_out(vty, " %-17pI4 %9d %9d %9d %11s\n",
+ &peer->addr, peer->recv_badpackets,
+ peer->recv_badroutes, ZEBRA_RIP_DISTANCE_DEFAULT,
+ rip_peer_uptime(peer, timebuf, RIP_UPTIME_LEN));
+ }
+}
+
+int rip_peer_list_cmp(struct rip_peer *p1, struct rip_peer *p2)
+{
+ if (p2->addr.s_addr == p1->addr.s_addr)
+ return 0;
+
+ return (htonl(p1->addr.s_addr) < htonl(p2->addr.s_addr)) ? -1 : 1;
+}
+
+void rip_peer_list_del(void *arg)
+{
+ rip_peer_free(arg);
+}
+
+void rip_peer_delete_routes(const struct rip_peer *peer)
+{
+ struct route_node *route_node;
+
+ for (route_node = route_top(peer->rip->table); route_node;
+ route_node = route_next(route_node)) {
+ struct rip_info *route_entry;
+ struct listnode *listnode;
+ struct listnode *listnode_next;
+ struct list *list;
+
+ list = route_node->info;
+ if (list == NULL)
+ continue;
+
+ for (ALL_LIST_ELEMENTS(list, listnode, listnode_next,
+ route_entry)) {
+ if (!rip_route_rte(route_entry))
+ continue;
+ if (route_entry->from.s_addr != peer->addr.s_addr)
+ continue;
+
+ if (listcount(list) == 1) {
+ EVENT_OFF(route_entry->t_timeout);
+ EVENT_OFF(route_entry->t_garbage_collect);
+ listnode_delete(list, route_entry);
+ if (list_isempty(list)) {
+ list_delete((struct list **)&route_node
+ ->info);
+ route_unlock_node(route_node);
+ }
+ rip_info_free(route_entry);
+
+ /* Signal the output process to trigger an
+ * update (see section 2.5). */
+ rip_event(peer->rip, RIP_TRIGGERED_UPDATE, 0);
+ } else
+ rip_ecmp_delete(peer->rip, route_entry);
+ break;
+ }
+ }
+}
diff --git a/ripd/rip_routemap.c b/ripd/rip_routemap.c
new file mode 100644
index 0000000..2ae8857
--- /dev/null
+++ b/ripd/rip_routemap.c
@@ -0,0 +1,581 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RIPv2 routemap.
+ * Copyright (C) 2005 6WIND <alain.ritoux@6wind.com>
+ * Copyright (C) 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "prefix.h"
+#include "vty.h"
+#include "routemap.h"
+#include "command.h"
+#include "filter.h"
+#include "log.h"
+#include "sockunion.h" /* for inet_aton () */
+#include "plist.h"
+#include "vrf.h"
+
+#include "ripd/ripd.h"
+
+struct rip_metric_modifier {
+ enum { metric_increment, metric_decrement, metric_absolute } type;
+ bool used;
+ uint8_t metric;
+};
+
+/* `match metric METRIC' */
+/* Match function return 1 if match is success else return zero. */
+static enum route_map_cmd_result_t
+route_match_metric(void *rule, const struct prefix *prefix, void *object)
+{
+ uint32_t *metric;
+ uint32_t check;
+ struct rip_info *rinfo;
+
+ metric = rule;
+ rinfo = object;
+
+ /* If external metric is available, the route-map should
+ work on this one (for redistribute purpose) */
+ check = (rinfo->external_metric) ? rinfo->external_metric
+ : rinfo->metric;
+ if (check == *metric)
+ return RMAP_MATCH;
+ else
+ return RMAP_NOMATCH;
+}
+
+/* Route map `match metric' match statement. `arg' is METRIC value */
+static void *route_match_metric_compile(const char *arg)
+{
+ uint32_t *metric;
+
+ metric = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint32_t));
+ *metric = atoi(arg);
+
+ if (*metric > 0)
+ return metric;
+
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, metric);
+ return NULL;
+}
+
+/* Free route map's compiled `match metric' value. */
+static void route_match_metric_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for metric matching. */
+static const struct route_map_rule_cmd route_match_metric_cmd = {
+ "metric",
+ route_match_metric,
+ route_match_metric_compile,
+ route_match_metric_free
+};
+
+/* `match interface IFNAME' */
+/* Match function return 1 if match is success else return zero. */
+static enum route_map_cmd_result_t
+route_match_interface(void *rule, const struct prefix *prefix, void *object)
+{
+ struct rip_info *rinfo;
+ struct interface *ifp;
+ char *ifname;
+
+ ifname = rule;
+ ifp = if_lookup_by_name(ifname, VRF_DEFAULT);
+
+ if (!ifp)
+ return RMAP_NOMATCH;
+
+ rinfo = object;
+
+ if (rinfo->ifindex_out == ifp->ifindex
+ || rinfo->nh.ifindex == ifp->ifindex)
+ return RMAP_MATCH;
+ else
+ return RMAP_NOMATCH;
+}
+
+/* Route map `match interface' match statement. `arg' is IFNAME value */
+/* XXX I don`t know if I need to check does interface exist? */
+static void *route_match_interface_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `match interface' value. */
+static void route_match_interface_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for interface matching. */
+static const struct route_map_rule_cmd route_match_interface_cmd = {
+ "interface",
+ route_match_interface,
+ route_match_interface_compile,
+ route_match_interface_free
+};
+
+/* `match ip next-hop IP_ACCESS_LIST' */
+
+/* Match function return 1 if match is success else return zero. */
+static enum route_map_cmd_result_t
+route_match_ip_next_hop(void *rule, const struct prefix *prefix, void *object)
+{
+ struct access_list *alist;
+ struct rip_info *rinfo;
+ struct prefix_ipv4 p;
+
+ rinfo = object;
+ p.family = AF_INET;
+ p.prefix = (rinfo->nh.gate.ipv4.s_addr != INADDR_ANY)
+ ? rinfo->nh.gate.ipv4
+ : rinfo->from;
+ p.prefixlen = IPV4_MAX_BITLEN;
+
+ alist = access_list_lookup(AFI_IP, (char *)rule);
+ if (alist == NULL)
+ return RMAP_NOMATCH;
+
+ return (access_list_apply(alist, &p) == FILTER_DENY ? RMAP_NOMATCH
+ : RMAP_MATCH);
+}
+
+/* Route map `ip next-hop' match statement. `arg' should be
+ access-list name. */
+static void *route_match_ip_next_hop_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `. */
+static void route_match_ip_next_hop_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip next-hop matching. */
+static const struct route_map_rule_cmd route_match_ip_next_hop_cmd = {
+ "ip next-hop",
+ route_match_ip_next_hop,
+ route_match_ip_next_hop_compile,
+ route_match_ip_next_hop_free
+};
+
+/* `match ip next-hop prefix-list PREFIX_LIST' */
+
+static enum route_map_cmd_result_t
+route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ struct prefix_list *plist;
+ struct rip_info *rinfo;
+ struct prefix_ipv4 p;
+
+ rinfo = object;
+ p.family = AF_INET;
+ p.prefix = (rinfo->nh.gate.ipv4.s_addr != INADDR_ANY)
+ ? rinfo->nh.gate.ipv4
+ : rinfo->from;
+ p.prefixlen = IPV4_MAX_BITLEN;
+
+ plist = prefix_list_lookup(AFI_IP, (char *)rule);
+ if (plist == NULL)
+ return RMAP_NOMATCH;
+
+ return (prefix_list_apply(plist, &p) == PREFIX_DENY ? RMAP_NOMATCH
+ : RMAP_MATCH);
+}
+
+static void *route_match_ip_next_hop_prefix_list_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void route_match_ip_next_hop_prefix_list_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd
+ route_match_ip_next_hop_prefix_list_cmd = {
+ "ip next-hop prefix-list",
+ route_match_ip_next_hop_prefix_list,
+ route_match_ip_next_hop_prefix_list_compile,
+ route_match_ip_next_hop_prefix_list_free
+};
+
+/* `match ip next-hop type <blackhole>' */
+
+static enum route_map_cmd_result_t
+route_match_ip_next_hop_type(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ struct rip_info *rinfo;
+
+ if (prefix->family == AF_INET) {
+ rinfo = (struct rip_info *)object;
+ if (!rinfo)
+ return RMAP_NOMATCH;
+
+ if (rinfo->nh.type == NEXTHOP_TYPE_BLACKHOLE)
+ return RMAP_MATCH;
+ }
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_ip_next_hop_type_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void route_match_ip_next_hop_type_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd
+ route_match_ip_next_hop_type_cmd = {
+ "ip next-hop type",
+ route_match_ip_next_hop_type,
+ route_match_ip_next_hop_type_compile,
+ route_match_ip_next_hop_type_free
+};
+
+/* `match ip address IP_ACCESS_LIST' */
+
+/* Match function should return 1 if match is success else return
+ zero. */
+static enum route_map_cmd_result_t
+route_match_ip_address(void *rule, const struct prefix *prefix, void *object)
+{
+ struct access_list *alist;
+
+ alist = access_list_lookup(AFI_IP, (char *)rule);
+ if (alist == NULL)
+ return RMAP_NOMATCH;
+
+ return (access_list_apply(alist, prefix) == FILTER_DENY ? RMAP_NOMATCH
+ : RMAP_MATCH);
+}
+
+/* Route map `ip address' match statement. `arg' should be
+ access-list name. */
+static void *route_match_ip_address_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `ip address' value. */
+static void route_match_ip_address_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip address matching. */
+static const struct route_map_rule_cmd route_match_ip_address_cmd = {
+ "ip address",
+ route_match_ip_address,
+ route_match_ip_address_compile,
+ route_match_ip_address_free
+};
+
+/* `match ip address prefix-list PREFIX_LIST' */
+
+static enum route_map_cmd_result_t
+route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ struct prefix_list *plist;
+
+ plist = prefix_list_lookup(AFI_IP, (char *)rule);
+ if (plist == NULL)
+ return RMAP_NOMATCH;
+
+ return (prefix_list_apply(plist, prefix) == PREFIX_DENY ? RMAP_NOMATCH
+ : RMAP_MATCH);
+}
+
+static void *route_match_ip_address_prefix_list_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void route_match_ip_address_prefix_list_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd
+ route_match_ip_address_prefix_list_cmd = {
+ "ip address prefix-list",
+ route_match_ip_address_prefix_list,
+ route_match_ip_address_prefix_list_compile,
+ route_match_ip_address_prefix_list_free
+};
+
+/* `match tag TAG' */
+/* Match function return 1 if match is success else return zero. */
+static enum route_map_cmd_result_t
+route_match_tag(void *rule, const struct prefix *p, void *object)
+{
+ route_tag_t *tag;
+ struct rip_info *rinfo;
+ route_tag_t rinfo_tag;
+
+ tag = rule;
+ rinfo = object;
+
+ /* The information stored by rinfo is host ordered. */
+ rinfo_tag = rinfo->tag;
+ if (rinfo_tag == *tag)
+ return RMAP_MATCH;
+ else
+ return RMAP_NOMATCH;
+}
+
+/* Route map commands for tag matching. */
+static const struct route_map_rule_cmd route_match_tag_cmd = {
+ "tag",
+ route_match_tag,
+ route_map_rule_tag_compile,
+ route_map_rule_tag_free,
+};
+
+/* `set metric METRIC' */
+
+/* Set metric to attribute. */
+static enum route_map_cmd_result_t
+route_set_metric(void *rule, const struct prefix *prefix, void *object)
+{
+ struct rip_metric_modifier *mod;
+ struct rip_info *rinfo;
+
+ mod = rule;
+ rinfo = object;
+
+ if (!mod->used)
+ return RMAP_OKAY;
+
+ if (mod->type == metric_increment)
+ rinfo->metric_out += mod->metric;
+ else if (mod->type == metric_decrement)
+ rinfo->metric_out -= mod->metric;
+ else if (mod->type == metric_absolute)
+ rinfo->metric_out = mod->metric;
+
+ if ((signed int)rinfo->metric_out < 1)
+ rinfo->metric_out = 1;
+ if (rinfo->metric_out > RIP_METRIC_INFINITY)
+ rinfo->metric_out = RIP_METRIC_INFINITY;
+
+ rinfo->metric_set = 1;
+ return RMAP_OKAY;
+}
+
+/* set metric compilation. */
+static void *route_set_metric_compile(const char *arg)
+{
+ int len;
+ const char *pnt;
+ long metric;
+ char *endptr = NULL;
+ struct rip_metric_modifier *mod;
+
+ mod = XMALLOC(MTYPE_ROUTE_MAP_COMPILED,
+ sizeof(struct rip_metric_modifier));
+ mod->used = false;
+
+ len = strlen(arg);
+ pnt = arg;
+
+ if (len == 0)
+ return mod;
+
+ /* Examine first character. */
+ if (arg[0] == '+') {
+ mod->type = metric_increment;
+ pnt++;
+ } else if (arg[0] == '-') {
+ mod->type = metric_decrement;
+ pnt++;
+ } else
+ mod->type = metric_absolute;
+
+ /* Check beginning with digit string. */
+ if (*pnt < '0' || *pnt > '9')
+ return mod;
+
+ /* Convert string to integer. */
+ metric = strtol(pnt, &endptr, 10);
+
+ if (*endptr != '\0' || metric < 0) {
+ return mod;
+ }
+ if (metric > RIP_METRIC_INFINITY) {
+ zlog_info(
+ "%s: Metric specified: %ld is greater than RIP_METRIC_INFINITY, using INFINITY instead",
+ __func__, metric);
+ mod->metric = RIP_METRIC_INFINITY;
+ } else
+ mod->metric = metric;
+
+ mod->used = true;
+
+ return mod;
+}
+
+/* Free route map's compiled `set metric' value. */
+static void route_set_metric_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Set metric rule structure. */
+static const struct route_map_rule_cmd route_set_metric_cmd = {
+ "metric",
+ route_set_metric,
+ route_set_metric_compile,
+ route_set_metric_free,
+};
+
+/* `set ip next-hop IP_ADDRESS' */
+
+/* Set nexthop to object. object must be pointer to struct attr. */
+static enum route_map_cmd_result_t
+route_set_ip_nexthop(void *rule, const struct prefix *prefix,
+
+ void *object)
+{
+ struct in_addr *address;
+ struct rip_info *rinfo;
+
+ /* Fetch routemap's rule information. */
+ address = rule;
+ rinfo = object;
+
+ /* Set next hop value. */
+ rinfo->nexthop_out = *address;
+
+ return RMAP_OKAY;
+}
+
+/* Route map `ip nexthop' compile function. Given string is converted
+ to struct in_addr structure. */
+static void *route_set_ip_nexthop_compile(const char *arg)
+{
+ int ret;
+ struct in_addr *address;
+
+ address = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct in_addr));
+
+ ret = inet_aton(arg, address);
+
+ if (ret == 0) {
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, address);
+ return NULL;
+ }
+
+ return address;
+}
+
+/* Free route map's compiled `ip nexthop' value. */
+static void route_set_ip_nexthop_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip nexthop set. */
+static const struct route_map_rule_cmd route_set_ip_nexthop_cmd = {
+ "ip next-hop",
+ route_set_ip_nexthop,
+ route_set_ip_nexthop_compile,
+ route_set_ip_nexthop_free
+};
+
+/* `set tag TAG' */
+
+/* Set tag to object. object must be pointer to struct attr. */
+static enum route_map_cmd_result_t
+route_set_tag(void *rule, const struct prefix *prefix, void *object)
+{
+ route_tag_t *tag;
+ struct rip_info *rinfo;
+
+ /* Fetch routemap's rule information. */
+ tag = rule;
+ rinfo = object;
+
+ /* Set next hop value. */
+ rinfo->tag_out = *tag;
+
+ return RMAP_OKAY;
+}
+
+/* Route map commands for tag set. */
+static const struct route_map_rule_cmd route_set_tag_cmd = {
+ "tag",
+ route_set_tag,
+ route_map_rule_tag_compile,
+ route_map_rule_tag_free
+};
+
+#define MATCH_STR "Match values from routing table\n"
+#define SET_STR "Set values in destination routing protocol\n"
+
+/* Route-map init */
+void rip_route_map_init(void)
+{
+ route_map_init();
+
+ route_map_match_interface_hook(generic_match_add);
+ route_map_no_match_interface_hook(generic_match_delete);
+
+ route_map_match_ip_address_hook(generic_match_add);
+ route_map_no_match_ip_address_hook(generic_match_delete);
+
+ route_map_match_ip_address_prefix_list_hook(generic_match_add);
+ route_map_no_match_ip_address_prefix_list_hook(generic_match_delete);
+
+ route_map_match_ip_next_hop_hook(generic_match_add);
+ route_map_no_match_ip_next_hop_hook(generic_match_delete);
+
+ route_map_match_ip_next_hop_prefix_list_hook(generic_match_add);
+ route_map_no_match_ip_next_hop_prefix_list_hook(generic_match_delete);
+
+ route_map_match_ip_next_hop_type_hook(generic_match_add);
+ route_map_no_match_ip_next_hop_type_hook(generic_match_delete);
+
+ route_map_match_metric_hook(generic_match_add);
+ route_map_no_match_metric_hook(generic_match_delete);
+
+ route_map_match_tag_hook(generic_match_add);
+ route_map_no_match_tag_hook(generic_match_delete);
+
+ route_map_set_ip_nexthop_hook(generic_set_add);
+ route_map_no_set_ip_nexthop_hook(generic_set_delete);
+
+ route_map_set_metric_hook(generic_set_add);
+ route_map_no_set_metric_hook(generic_set_delete);
+
+ route_map_set_tag_hook(generic_set_add);
+ route_map_no_set_tag_hook(generic_set_delete);
+
+ route_map_install_match(&route_match_metric_cmd);
+ route_map_install_match(&route_match_interface_cmd);
+ route_map_install_match(&route_match_ip_next_hop_cmd);
+ route_map_install_match(&route_match_ip_next_hop_prefix_list_cmd);
+ route_map_install_match(&route_match_ip_next_hop_type_cmd);
+ route_map_install_match(&route_match_ip_address_cmd);
+ route_map_install_match(&route_match_ip_address_prefix_list_cmd);
+ route_map_install_match(&route_match_tag_cmd);
+
+ route_map_install_set(&route_set_metric_cmd);
+ route_map_install_set(&route_set_ip_nexthop_cmd);
+ route_map_install_set(&route_set_tag_cmd);
+}
diff --git a/ripd/rip_snmp.c b/ripd/rip_snmp.c
new file mode 100644
index 0000000..0e5d4d5
--- /dev/null
+++ b/ripd/rip_snmp.c
@@ -0,0 +1,577 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RIP SNMP support
+ * Copyright (C) 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
+ */
+
+#include <zebra.h>
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+
+#include "if.h"
+#include "vrf.h"
+#include "log.h"
+#include "prefix.h"
+#include "command.h"
+#include "table.h"
+#include "smux.h"
+#include "libfrr.h"
+#include "lib/version.h"
+
+#include "ripd/ripd.h"
+
+/* RIPv2-MIB. */
+#define RIPV2MIB 1,3,6,1,2,1,23
+
+/* RIPv2-MIB rip2Globals values. */
+#define RIP2GLOBALROUTECHANGES 1
+#define RIP2GLOBALQUERIES 2
+
+/* RIPv2-MIB rip2IfStatEntry. */
+#define RIP2IFSTATENTRY 1
+
+/* RIPv2-MIB rip2IfStatTable. */
+#define RIP2IFSTATADDRESS 1
+#define RIP2IFSTATRCVBADPACKETS 2
+#define RIP2IFSTATRCVBADROUTES 3
+#define RIP2IFSTATSENTUPDATES 4
+#define RIP2IFSTATSTATUS 5
+
+/* RIPv2-MIB rip2IfConfTable. */
+#define RIP2IFCONFADDRESS 1
+#define RIP2IFCONFDOMAIN 2
+#define RIP2IFCONFAUTHTYPE 3
+#define RIP2IFCONFAUTHKEY 4
+#define RIP2IFCONFSEND 5
+#define RIP2IFCONFRECEIVE 6
+#define RIP2IFCONFDEFAULTMETRIC 7
+#define RIP2IFCONFSTATUS 8
+#define RIP2IFCONFSRCADDRESS 9
+
+/* RIPv2-MIB rip2PeerTable. */
+#define RIP2PEERADDRESS 1
+#define RIP2PEERDOMAIN 2
+#define RIP2PEERLASTUPDATE 3
+#define RIP2PEERVERSION 4
+#define RIP2PEERRCVBADPACKETS 5
+#define RIP2PEERRCVBADROUTES 6
+
+/* SNMP value hack. */
+#define COUNTER ASN_COUNTER
+#define INTEGER ASN_INTEGER
+#define TIMETICKS ASN_TIMETICKS
+#define IPADDRESS ASN_IPADDRESS
+#define STRING ASN_OCTET_STR
+
+/* Define SNMP local variables. */
+SNMP_LOCAL_VARIABLES
+
+/* RIP-MIB instances. */
+static oid rip_oid[] = {RIPV2MIB};
+
+/* Interface cache table sorted by interface's address. */
+static struct route_table *rip_ifaddr_table;
+
+/* Hook functions. */
+static uint8_t *rip2Globals(struct variable *, oid[], size_t *, int, size_t *,
+ WriteMethod **);
+static uint8_t *rip2IfStatEntry(struct variable *, oid[], size_t *, int,
+ size_t *, WriteMethod **);
+static uint8_t *rip2IfConfAddress(struct variable *, oid[], size_t *, int,
+ size_t *, WriteMethod **);
+static uint8_t *rip2PeerTable(struct variable *, oid[], size_t *, int, size_t *,
+ WriteMethod **);
+
+static struct variable rip_variables[] = {
+ /* RIP Global Counters. */
+ {RIP2GLOBALROUTECHANGES, COUNTER, RONLY, rip2Globals, 2, {1, 1}},
+ {RIP2GLOBALQUERIES, COUNTER, RONLY, rip2Globals, 2, {1, 2}},
+ /* RIP Interface Tables. */
+ {RIP2IFSTATADDRESS, IPADDRESS, RONLY, rip2IfStatEntry, 3, {2, 1, 1}},
+ {RIP2IFSTATRCVBADPACKETS,
+ COUNTER,
+ RONLY,
+ rip2IfStatEntry,
+ 3,
+ {2, 1, 2}},
+ {RIP2IFSTATRCVBADROUTES, COUNTER, RONLY, rip2IfStatEntry, 3, {2, 1, 3}},
+ {RIP2IFSTATSENTUPDATES, COUNTER, RONLY, rip2IfStatEntry, 3, {2, 1, 4}},
+ {RIP2IFSTATSTATUS, COUNTER, RWRITE, rip2IfStatEntry, 3, {2, 1, 5}},
+ {RIP2IFCONFADDRESS,
+ IPADDRESS,
+ RONLY,
+ rip2IfConfAddress,
+ /* RIP Interface Configuration Table. */
+ 3,
+ {3, 1, 1}},
+ {RIP2IFCONFDOMAIN, STRING, RONLY, rip2IfConfAddress, 3, {3, 1, 2}},
+ {RIP2IFCONFAUTHTYPE, COUNTER, RONLY, rip2IfConfAddress, 3, {3, 1, 3}},
+ {RIP2IFCONFAUTHKEY, STRING, RONLY, rip2IfConfAddress, 3, {3, 1, 4}},
+ {RIP2IFCONFSEND, COUNTER, RONLY, rip2IfConfAddress, 3, {3, 1, 5}},
+ {RIP2IFCONFRECEIVE, COUNTER, RONLY, rip2IfConfAddress, 3, {3, 1, 6}},
+ {RIP2IFCONFDEFAULTMETRIC,
+ COUNTER,
+ RONLY,
+ rip2IfConfAddress,
+ 3,
+ {3, 1, 7}},
+ {RIP2IFCONFSTATUS, COUNTER, RONLY, rip2IfConfAddress, 3, {3, 1, 8}},
+ {RIP2IFCONFSRCADDRESS,
+ IPADDRESS,
+ RONLY,
+ rip2IfConfAddress,
+ 3,
+ {3, 1, 9}},
+ {RIP2PEERADDRESS,
+ IPADDRESS,
+ RONLY,
+ rip2PeerTable,
+ /* RIP Peer Table. */
+ 3,
+ {4, 1, 1}},
+ {RIP2PEERDOMAIN, STRING, RONLY, rip2PeerTable, 3, {4, 1, 2}},
+ {RIP2PEERLASTUPDATE, TIMETICKS, RONLY, rip2PeerTable, 3, {4, 1, 3}},
+ {RIP2PEERVERSION, INTEGER, RONLY, rip2PeerTable, 3, {4, 1, 4}},
+ {RIP2PEERRCVBADPACKETS, COUNTER, RONLY, rip2PeerTable, 3, {4, 1, 5}},
+ {RIP2PEERRCVBADROUTES, COUNTER, RONLY, rip2PeerTable, 3, {4, 1, 6}}};
+
+extern struct event_loop *master;
+
+static uint8_t *rip2Globals(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct rip *rip;
+
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ rip = rip_lookup_by_vrf_id(VRF_DEFAULT);
+ if (!rip)
+ return NULL;
+
+ /* Return global counter. */
+ switch (v->magic) {
+ case RIP2GLOBALROUTECHANGES:
+ return SNMP_INTEGER(rip->counters.route_changes);
+ case RIP2GLOBALQUERIES:
+ return SNMP_INTEGER(rip->counters.queries);
+ default:
+ return NULL;
+ }
+ return NULL;
+}
+
+static int rip_snmp_ifaddr_add(struct connected *ifc)
+{
+ struct interface *ifp = ifc->ifp;
+ struct prefix *p;
+ struct route_node *rn;
+
+ p = ifc->address;
+
+ if (p->family != AF_INET)
+ return 0;
+
+ rn = route_node_get(rip_ifaddr_table, p);
+ rn->info = ifp;
+ return 0;
+}
+
+static int rip_snmp_ifaddr_del(struct connected *ifc)
+{
+ struct interface *ifp = ifc->ifp;
+ struct prefix *p;
+ struct route_node *rn;
+ struct interface *i;
+
+ p = ifc->address;
+
+ if (p->family != AF_INET)
+ return 0;
+
+ rn = route_node_lookup(rip_ifaddr_table, p);
+ if (!rn)
+ return 0;
+ i = rn->info;
+ if (!strncmp(i->name, ifp->name, INTERFACE_NAMSIZ)) {
+ rn->info = NULL;
+ route_unlock_node(rn);
+ route_unlock_node(rn);
+ }
+ return 0;
+}
+
+static struct interface *rip_ifaddr_lookup_next(struct in_addr *addr)
+{
+ struct prefix_ipv4 p;
+ struct route_node *rn;
+ struct interface *ifp;
+
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_BITLEN;
+ p.prefix = *addr;
+
+ rn = route_node_get(rip_ifaddr_table, (struct prefix *)&p);
+
+ for (rn = route_next(rn); rn; rn = route_next(rn))
+ if (rn->info)
+ break;
+
+ if (rn && rn->info) {
+ ifp = rn->info;
+ *addr = rn->p.u.prefix4;
+ route_unlock_node(rn);
+ return ifp;
+ }
+ return NULL;
+}
+
+static struct interface *rip2IfLookup(struct variable *v, oid name[],
+ size_t *length, struct in_addr *addr,
+ int exact)
+{
+ int len;
+ struct interface *ifp;
+
+ if (exact) {
+ /* Check the length. */
+ if (*length - v->namelen != sizeof(struct in_addr))
+ return NULL;
+
+ oid2in_addr(name + v->namelen, sizeof(struct in_addr), addr);
+
+ return if_lookup_address_local((void *)addr, AF_INET,
+ VRF_DEFAULT);
+ } else {
+ len = *length - v->namelen;
+ if (len > 4)
+ len = 4;
+
+ oid2in_addr(name + v->namelen, len, addr);
+
+ ifp = rip_ifaddr_lookup_next(addr);
+
+ if (ifp == NULL)
+ return NULL;
+
+ oid_copy_in_addr(name + v->namelen, addr);
+
+ *length = v->namelen + sizeof(struct in_addr);
+
+ return ifp;
+ }
+ return NULL;
+}
+
+static struct rip_peer *rip2PeerLookup(struct variable *v, oid name[],
+ size_t *length, struct in_addr *addr,
+ int exact)
+{
+ struct rip *rip;
+ int len;
+ struct rip_peer *peer;
+
+ rip = rip_lookup_by_vrf_id(VRF_DEFAULT);
+ if (!rip)
+ return NULL;
+
+ if (exact) {
+ /* Check the length. */
+ if (*length - v->namelen != sizeof(struct in_addr) + 1)
+ return NULL;
+
+ oid2in_addr(name + v->namelen, sizeof(struct in_addr), addr);
+
+ peer = rip_peer_lookup(rip, addr);
+
+ if (peer->domain
+ == (int)name[v->namelen + sizeof(struct in_addr)])
+ return peer;
+
+ return NULL;
+ } else {
+ len = *length - v->namelen;
+ if (len > 4)
+ len = 4;
+
+ oid2in_addr(name + v->namelen, len, addr);
+
+ len = *length - v->namelen;
+ peer = rip_peer_lookup(rip, addr);
+ if (peer) {
+ if ((len < (int)sizeof(struct in_addr) + 1)
+ || (peer->domain
+ > (int)name[v->namelen
+ + sizeof(struct in_addr)])) {
+ oid_copy_in_addr(name + v->namelen,
+ &peer->addr);
+ name[v->namelen + sizeof(struct in_addr)] =
+ peer->domain;
+ *length =
+ sizeof(struct in_addr) + v->namelen + 1;
+ return peer;
+ }
+ }
+ peer = rip_peer_lookup_next(rip, addr);
+
+ if (!peer)
+ return NULL;
+
+ oid_copy_in_addr(name + v->namelen, &peer->addr);
+ name[v->namelen + sizeof(struct in_addr)] = peer->domain;
+ *length = sizeof(struct in_addr) + v->namelen + 1;
+
+ return peer;
+ }
+ return NULL;
+}
+
+static uint8_t *rip2IfStatEntry(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+ static struct in_addr addr;
+ static long valid = SNMP_VALID;
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ memset(&addr, 0, sizeof(addr));
+
+ /* Lookup interface. */
+ ifp = rip2IfLookup(v, name, length, &addr, exact);
+ if (!ifp)
+ return NULL;
+
+ /* Fetch rip_interface information. */
+ ri = ifp->info;
+
+ switch (v->magic) {
+ case RIP2IFSTATADDRESS:
+ return SNMP_IPADDRESS(addr);
+ case RIP2IFSTATRCVBADPACKETS:
+ *var_len = sizeof(long);
+ return (uint8_t *)&ri->recv_badpackets;
+
+ case RIP2IFSTATRCVBADROUTES:
+ *var_len = sizeof(long);
+ return (uint8_t *)&ri->recv_badroutes;
+
+ case RIP2IFSTATSENTUPDATES:
+ *var_len = sizeof(long);
+ return (uint8_t *)&ri->sent_updates;
+
+ case RIP2IFSTATSTATUS:
+ *var_len = sizeof(long);
+ v->type = ASN_INTEGER;
+ return (uint8_t *)&valid;
+
+ default:
+ return NULL;
+ }
+ return NULL;
+}
+
+static long rip2IfConfSend(struct rip_interface *ri)
+{
+#define doNotSend 1
+#define ripVersion1 2
+#define rip1Compatible 3
+#define ripVersion2 4
+#define ripV1Demand 5
+#define ripV2Demand 6
+
+ if (!ri->running)
+ return doNotSend;
+
+ if (ri->ri_send & RIPv2)
+ return ripVersion2;
+ else if (ri->ri_send & RIPv1)
+ return ripVersion1;
+ else if (ri->rip) {
+ if (ri->rip->version_send == RIPv2)
+ return ripVersion2;
+ else if (ri->rip->version_send == RIPv1)
+ return ripVersion1;
+ }
+ return doNotSend;
+}
+
+static long rip2IfConfReceive(struct rip_interface *ri)
+{
+#define rip1 1
+#define rip2 2
+#define rip1OrRip2 3
+#define doNotReceive 4
+
+ int recvv;
+
+ if (!ri->running)
+ return doNotReceive;
+
+ recvv = (ri->ri_receive == RI_RIP_UNSPEC) ? ri->rip->version_recv
+ : ri->ri_receive;
+ if (recvv == RI_RIP_VERSION_1_AND_2)
+ return rip1OrRip2;
+ else if (recvv & RIPv2)
+ return rip2;
+ else if (recvv & RIPv1)
+ return rip1;
+ else
+ return doNotReceive;
+}
+
+static uint8_t *rip2IfConfAddress(struct variable *v, oid name[],
+ size_t *length, int exact, size_t *val_len,
+ WriteMethod **write_method)
+{
+ static struct in_addr addr;
+ static long valid = SNMP_INVALID;
+ static long domain = 0;
+ static long config = 0;
+ static unsigned int auth = 0;
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (smux_header_table(v, name, length, exact, val_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ memset(&addr, 0, sizeof(addr));
+
+ /* Lookup interface. */
+ ifp = rip2IfLookup(v, name, length, &addr, exact);
+ if (!ifp)
+ return NULL;
+
+ /* Fetch rip_interface information. */
+ ri = ifp->info;
+
+ switch (v->magic) {
+ case RIP2IFCONFADDRESS:
+ *val_len = sizeof(struct in_addr);
+ return (uint8_t *)&addr;
+
+ case RIP2IFCONFDOMAIN:
+ *val_len = 2;
+ return (uint8_t *)&domain;
+
+ case RIP2IFCONFAUTHTYPE:
+ auth = ri->auth_type;
+ *val_len = sizeof(long);
+ v->type = ASN_INTEGER;
+ return (uint8_t *)&auth;
+
+ case RIP2IFCONFAUTHKEY:
+ *val_len = 0;
+ return (uint8_t *)&domain;
+ case RIP2IFCONFSEND:
+ config = rip2IfConfSend(ri);
+ *val_len = sizeof(long);
+ v->type = ASN_INTEGER;
+ return (uint8_t *)&config;
+ case RIP2IFCONFRECEIVE:
+ config = rip2IfConfReceive(ri);
+ *val_len = sizeof(long);
+ v->type = ASN_INTEGER;
+ return (uint8_t *)&config;
+
+ case RIP2IFCONFDEFAULTMETRIC:
+ *val_len = sizeof(long);
+ v->type = ASN_INTEGER;
+ return (uint8_t *)&ifp->metric;
+ case RIP2IFCONFSTATUS:
+ *val_len = sizeof(long);
+ v->type = ASN_INTEGER;
+ return (uint8_t *)&valid;
+ case RIP2IFCONFSRCADDRESS:
+ *val_len = sizeof(struct in_addr);
+ return (uint8_t *)&addr;
+
+ default:
+ return NULL;
+ }
+ return NULL;
+}
+
+static uint8_t *rip2PeerTable(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *val_len,
+ WriteMethod **write_method)
+{
+ static struct in_addr addr;
+ static int domain = 0;
+ static int version;
+ /* static time_t uptime; */
+
+ struct rip_peer *peer;
+
+ if (smux_header_table(v, name, length, exact, val_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ memset(&addr, 0, sizeof(addr));
+
+ /* Lookup interface. */
+ peer = rip2PeerLookup(v, name, length, &addr, exact);
+ if (!peer)
+ return NULL;
+
+ switch (v->magic) {
+ case RIP2PEERADDRESS:
+ *val_len = sizeof(struct in_addr);
+ return (uint8_t *)&peer->addr;
+
+ case RIP2PEERDOMAIN:
+ *val_len = 2;
+ return (uint8_t *)&domain;
+
+ case RIP2PEERLASTUPDATE:
+ return (uint8_t *)NULL;
+
+ case RIP2PEERVERSION:
+ *val_len = sizeof(int);
+ version = peer->version;
+ return (uint8_t *)&version;
+
+ case RIP2PEERRCVBADPACKETS:
+ *val_len = sizeof(int);
+ return (uint8_t *)&peer->recv_badpackets;
+
+ case RIP2PEERRCVBADROUTES:
+ *val_len = sizeof(int);
+ return (uint8_t *)&peer->recv_badroutes;
+
+ default:
+ return NULL;
+ }
+ return NULL;
+}
+
+/* Register RIPv2-MIB. */
+static int rip_snmp_init(struct event_loop *master)
+{
+ rip_ifaddr_table = route_table_init();
+
+ smux_init(master);
+ REGISTER_MIB("mibII/rip", rip_variables, variable, rip_oid);
+ return 0;
+}
+
+static int rip_snmp_module_init(void)
+{
+ hook_register(rip_ifaddr_add, rip_snmp_ifaddr_add);
+ hook_register(rip_ifaddr_del, rip_snmp_ifaddr_del);
+
+ hook_register(frr_late_init, rip_snmp_init);
+ return 0;
+}
+
+FRR_MODULE_SETUP(.name = "ripd_snmp", .version = FRR_VERSION,
+ .description = "ripd AgentX SNMP module",
+ .init = rip_snmp_module_init,
+);
diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c
new file mode 100644
index 0000000..36b58cb
--- /dev/null
+++ b/ripd/rip_zebra.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RIPd and zebra interface.
+ * Copyright (C) 1997, 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "prefix.h"
+#include "table.h"
+#include "stream.h"
+#include "memory.h"
+#include "zclient.h"
+#include "log.h"
+#include "vrf.h"
+#include "bfd.h"
+#include "ripd/ripd.h"
+#include "ripd/rip_debug.h"
+#include "ripd/rip_interface.h"
+
+/* All information about zebra. */
+struct zclient *zclient = NULL;
+
+/* Send ECMP routes to zebra. */
+static void rip_zebra_ipv4_send(struct rip *rip, struct route_node *rp,
+ uint8_t cmd)
+{
+ struct list *list = (struct list *)rp->info;
+ struct zapi_route api;
+ struct zapi_nexthop *api_nh;
+ struct listnode *listnode = NULL;
+ struct rip_info *rinfo = NULL;
+ uint32_t count = 0;
+
+ memset(&api, 0, sizeof(api));
+ api.vrf_id = rip->vrf->vrf_id;
+ api.type = ZEBRA_ROUTE_RIP;
+ api.safi = SAFI_UNICAST;
+
+ SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
+ for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) {
+ if (count >= zebra_ecmp_count)
+ break;
+ api_nh = &api.nexthops[count];
+ api_nh->vrf_id = rip->vrf->vrf_id;
+ api_nh->gate = rinfo->nh.gate;
+ api_nh->type = NEXTHOP_TYPE_IPV4;
+ if (cmd == ZEBRA_ROUTE_ADD)
+ SET_FLAG(rinfo->flags, RIP_RTF_FIB);
+ else
+ UNSET_FLAG(rinfo->flags, RIP_RTF_FIB);
+ count++;
+ }
+
+ api.prefix = rp->p;
+ api.nexthop_num = count;
+
+ rinfo = listgetdata(listhead(list));
+
+ SET_FLAG(api.message, ZAPI_MESSAGE_METRIC);
+ api.metric = rinfo->metric;
+
+ if (rinfo->distance && rinfo->distance != ZEBRA_RIP_DISTANCE_DEFAULT) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE);
+ api.distance = rinfo->distance;
+ }
+
+ if (rinfo->tag) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_TAG);
+ api.tag = rinfo->tag;
+ }
+
+ zclient_route_send(cmd, zclient, &api);
+
+ if (IS_RIP_DEBUG_ZEBRA) {
+ if (rip->ecmp)
+ zlog_debug("%s: %pFX nexthops %d",
+ (cmd == ZEBRA_ROUTE_ADD)
+ ? "Install into zebra"
+ : "Delete from zebra",
+ &rp->p, count);
+ else
+ zlog_debug("%s: %pFX",
+ (cmd == ZEBRA_ROUTE_ADD)
+ ? "Install into zebra"
+ : "Delete from zebra", &rp->p);
+ }
+
+ rip->counters.route_changes++;
+}
+
+/* Add/update ECMP routes to zebra. */
+void rip_zebra_ipv4_add(struct rip *rip, struct route_node *rp)
+{
+ rip_zebra_ipv4_send(rip, rp, ZEBRA_ROUTE_ADD);
+}
+
+/* Delete ECMP routes from zebra. */
+void rip_zebra_ipv4_delete(struct rip *rip, struct route_node *rp)
+{
+ rip_zebra_ipv4_send(rip, rp, ZEBRA_ROUTE_DELETE);
+}
+
+/* Zebra route add and delete treatment. */
+static int rip_zebra_read_route(ZAPI_CALLBACK_ARGS)
+{
+ struct rip *rip;
+ struct zapi_route api;
+ struct nexthop nh;
+
+ rip = rip_lookup_by_vrf_id(vrf_id);
+ if (!rip)
+ return 0;
+
+ if (zapi_route_decode(zclient->ibuf, &api) < 0)
+ return -1;
+
+ memset(&nh, 0, sizeof(nh));
+ nh.type = api.nexthops[0].type;
+ nh.gate.ipv4 = api.nexthops[0].gate.ipv4;
+ nh.ifindex = api.nexthops[0].ifindex;
+
+ /* Then fetch IPv4 prefixes. */
+ if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
+ rip_redistribute_add(rip, api.type, RIP_ROUTE_REDISTRIBUTE,
+ (struct prefix_ipv4 *)&api.prefix, &nh,
+ api.metric, api.distance, api.tag);
+ else if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_DEL)
+ rip_redistribute_delete(rip, api.type, RIP_ROUTE_REDISTRIBUTE,
+ (struct prefix_ipv4 *)&api.prefix,
+ nh.ifindex);
+
+ return 0;
+}
+
+void rip_redistribute_conf_update(struct rip *rip, int type)
+{
+ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP,
+ type, 0, rip->vrf->vrf_id);
+}
+
+void rip_redistribute_conf_delete(struct rip *rip, int type)
+{
+ if (zclient->sock > 0)
+ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_DELETE, zclient,
+ AFI_IP, type, 0, rip->vrf->vrf_id);
+
+ /* Remove the routes from RIP table. */
+ rip_redistribute_withdraw(rip, type);
+}
+
+int rip_redistribute_check(struct rip *rip, int type)
+{
+ return rip->redist[type].enabled;
+}
+
+void rip_redistribute_enable(struct rip *rip)
+{
+ for (int i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+ if (!rip_redistribute_check(rip, i))
+ continue;
+
+ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP,
+ i, 0, rip->vrf->vrf_id);
+ }
+}
+
+void rip_redistribute_disable(struct rip *rip)
+{
+ for (int i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+ if (!rip_redistribute_check(rip, i))
+ continue;
+
+ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_DELETE, zclient,
+ AFI_IP, i, 0, rip->vrf->vrf_id);
+ }
+}
+
+void rip_show_redistribute_config(struct vty *vty, struct rip *rip)
+{
+ for (int i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+ if (i == zclient->redist_default
+ || !rip_redistribute_check(rip, i))
+ continue;
+
+ vty_out(vty, " %s", zebra_route_string(i));
+ }
+}
+
+void rip_zebra_vrf_register(struct vrf *vrf)
+{
+ if (vrf->vrf_id == VRF_DEFAULT)
+ return;
+
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("%s: register VRF %s(%u) to zebra", __func__,
+ vrf->name, vrf->vrf_id);
+
+ zclient_send_reg_requests(zclient, vrf->vrf_id);
+ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf->vrf_id);
+}
+
+void rip_zebra_vrf_deregister(struct vrf *vrf)
+{
+ if (vrf->vrf_id == VRF_DEFAULT)
+ return;
+
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("%s: deregister VRF %s(%u) from zebra.", __func__,
+ vrf->name, vrf->vrf_id);
+
+ zclient_send_dereg_requests(zclient, vrf->vrf_id);
+ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_DEREGISTER, vrf->vrf_id);
+}
+
+static void rip_zebra_connected(struct zclient *zclient)
+{
+ zclient_send_reg_requests(zclient, VRF_DEFAULT);
+ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
+}
+
+zclient_handler *const rip_handlers[] = {
+ [ZEBRA_INTERFACE_ADDRESS_ADD] = rip_interface_address_add,
+ [ZEBRA_INTERFACE_ADDRESS_DELETE] = rip_interface_address_delete,
+ [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = rip_zebra_read_route,
+ [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = rip_zebra_read_route,
+};
+
+static void rip_zebra_capabilities(struct zclient_capabilities *cap)
+{
+ zebra_ecmp_count = MIN(cap->ecmp, zebra_ecmp_count);
+}
+
+void rip_zclient_init(struct event_loop *master)
+{
+ /* Set default value to the zebra client structure. */
+ zclient = zclient_new(master, &zclient_options_default, rip_handlers,
+ array_size(rip_handlers));
+ zclient_init(zclient, ZEBRA_ROUTE_RIP, 0, &ripd_privs);
+ zclient->zebra_connected = rip_zebra_connected;
+ zclient->zebra_capabilities = rip_zebra_capabilities;
+}
+
+void rip_zclient_stop(void)
+{
+ zclient_stop(zclient);
+ zclient_free(zclient);
+}
diff --git a/ripd/ripd.c b/ripd/ripd.c
new file mode 100644
index 0000000..7bfcaad
--- /dev/null
+++ b/ripd/ripd.c
@@ -0,0 +1,3695 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RIP version 1 and 2.
+ * Copyright (C) 2005 6WIND <alain.ritoux@6wind.com>
+ * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro <kunihiro@zebra.org>
+ */
+
+#include <zebra.h>
+
+#include "vrf.h"
+#include "if.h"
+#include "command.h"
+#include "prefix.h"
+#include "table.h"
+#include "frrevent.h"
+#include "memory.h"
+#include "log.h"
+#include "stream.h"
+#include "filter.h"
+#include "sockunion.h"
+#include "sockopt.h"
+#include "routemap.h"
+#include "if_rmap.h"
+#include "plist.h"
+#include "distribute.h"
+#ifdef CRYPTO_INTERNAL
+#include "md5.h"
+#endif
+#include "keychain.h"
+#include "privs.h"
+#include "lib_errors.h"
+#include "northbound_cli.h"
+#include "network.h"
+#include "lib/printfrr.h"
+
+#include "ripd/ripd.h"
+#include "ripd/rip_nb.h"
+#include "ripd/rip_bfd.h"
+#include "ripd/rip_debug.h"
+#include "ripd/rip_errors.h"
+#include "ripd/rip_interface.h"
+
+/* UDP receive buffer size */
+#define RIP_UDP_RCV_BUF 41600
+
+DEFINE_MGROUP(RIPD, "ripd");
+DEFINE_MTYPE_STATIC(RIPD, RIP, "RIP structure");
+DEFINE_MTYPE_STATIC(RIPD, RIP_VRF_NAME, "RIP VRF name");
+DEFINE_MTYPE_STATIC(RIPD, RIP_INFO, "RIP route info");
+DEFINE_MTYPE_STATIC(RIPD, RIP_DISTANCE, "RIP distance");
+
+/* Prototypes. */
+static void rip_output_process(struct connected *, struct sockaddr_in *, int,
+ uint8_t);
+static void rip_triggered_update(struct event *);
+static int rip_update_jitter(unsigned long);
+static void rip_distance_table_node_cleanup(struct route_table *table,
+ struct route_node *node);
+static void rip_instance_enable(struct rip *rip, struct vrf *vrf, int sock);
+static void rip_instance_disable(struct rip *rip);
+
+static void rip_distribute_update(struct distribute_ctx *ctx,
+ struct distribute *dist);
+
+static void rip_if_rmap_update(struct if_rmap_ctx *ctx,
+ struct if_rmap *if_rmap);
+
+/* RIP output routes type. */
+enum { rip_all_route, rip_changed_route };
+
+/* RIP command strings. */
+static const struct message rip_msg[] = {{RIP_REQUEST, "REQUEST"},
+ {RIP_RESPONSE, "RESPONSE"},
+ {RIP_TRACEON, "TRACEON"},
+ {RIP_TRACEOFF, "TRACEOFF"},
+ {RIP_POLL, "POLL"},
+ {RIP_POLL_ENTRY, "POLL ENTRY"},
+ {0}};
+
+/* Generate rb-tree of RIP instances. */
+static inline int rip_instance_compare(const struct rip *a, const struct rip *b)
+{
+ return strcmp(a->vrf_name, b->vrf_name);
+}
+RB_GENERATE(rip_instance_head, rip, entry, rip_instance_compare)
+
+struct rip_instance_head rip_instances = RB_INITIALIZER(&rip_instances);
+
+/* Utility function to set broadcast option to the socket. */
+static int sockopt_broadcast(int sock)
+{
+ int ret;
+ int on = 1;
+
+ ret = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&on,
+ sizeof(on));
+ if (ret < 0) {
+ zlog_warn("can't set sockopt SO_BROADCAST to socket %d", sock);
+ return -1;
+ }
+ return 0;
+}
+
+int rip_route_rte(struct rip_info *rinfo)
+{
+ return (rinfo->type == ZEBRA_ROUTE_RIP
+ && rinfo->sub_type == RIP_ROUTE_RTE);
+}
+
+static struct rip_info *rip_info_new(void)
+{
+ return XCALLOC(MTYPE_RIP_INFO, sizeof(struct rip_info));
+}
+
+void rip_info_free(struct rip_info *rinfo)
+{
+ XFREE(MTYPE_RIP_INFO, rinfo);
+}
+
+struct rip *rip_info_get_instance(const struct rip_info *rinfo)
+{
+ return route_table_get_info(rinfo->rp->table);
+}
+
+/* RIP route garbage collect timer. */
+static void rip_garbage_collect(struct event *t)
+{
+ struct rip_info *rinfo;
+ struct route_node *rp;
+
+ rinfo = EVENT_ARG(t);
+
+ /* Off timeout timer. */
+ EVENT_OFF(rinfo->t_timeout);
+
+ /* Get route_node pointer. */
+ rp = rinfo->rp;
+
+ /* Unlock route_node. */
+ listnode_delete(rp->info, rinfo);
+ if (list_isempty((struct list *)rp->info)) {
+ list_delete((struct list **)&rp->info);
+ route_unlock_node(rp);
+ }
+
+ /* Free RIP routing information. */
+ rip_info_free(rinfo);
+}
+
+static void rip_timeout_update(struct rip *rip, struct rip_info *rinfo);
+
+/* Add new route to the ECMP list.
+ * RETURN: the new entry added in the list, or NULL if it is not the first
+ * entry and ECMP is not allowed.
+ */
+struct rip_info *rip_ecmp_add(struct rip *rip, struct rip_info *rinfo_new)
+{
+ struct route_node *rp = rinfo_new->rp;
+ struct rip_info *rinfo = NULL;
+ struct rip_info *rinfo_exist = NULL;
+ struct list *list = NULL;
+ struct listnode *node = NULL;
+ struct listnode *nnode = NULL;
+
+ if (rp->info == NULL)
+ rp->info = list_new();
+ list = (struct list *)rp->info;
+
+ /* If ECMP is not allowed and some entry already exists in the list,
+ * do nothing. */
+ if (listcount(list) && !rip->ecmp)
+ return NULL;
+
+ /* Add or replace an existing ECMP path with lower neighbor IP */
+ if (listcount(list) && listcount(list) >= rip->ecmp) {
+ struct rip_info *from_highest = NULL;
+
+ /* Find the rip_info struct that has the highest nexthop IP */
+ for (ALL_LIST_ELEMENTS(list, node, nnode, rinfo_exist))
+ if (!from_highest ||
+ (from_highest &&
+ IPV4_ADDR_CMP(&rinfo_exist->from,
+ &from_highest->from) > 0)) {
+ from_highest = rinfo_exist;
+ }
+
+ /* If we have a route in ECMP group, delete the old
+ * one that has a higher next-hop address. Lower IP is
+ * preferred.
+ */
+ if (rip->ecmp > 1 && from_highest &&
+ IPV4_ADDR_CMP(&from_highest->from, &rinfo_new->from) > 0) {
+ rip_ecmp_delete(rip, from_highest);
+ goto add_or_replace;
+ }
+
+ return NULL;
+ }
+
+add_or_replace:
+ rinfo = rip_info_new();
+ memcpy(rinfo, rinfo_new, sizeof(struct rip_info));
+ listnode_add(list, rinfo);
+
+ if (rip_route_rte(rinfo)) {
+ rip_timeout_update(rip, rinfo);
+ rip_zebra_ipv4_add(rip, rp);
+ }
+
+ /* Set the route change flag on the first entry. */
+ rinfo = listgetdata(listhead(list));
+ SET_FLAG(rinfo->flags, RIP_RTF_CHANGED);
+
+ /* Signal the output process to trigger an update (see section 2.5). */
+ rip_event(rip, RIP_TRIGGERED_UPDATE, 0);
+
+ return rinfo;
+}
+
+/* Replace the ECMP list with the new route.
+ * RETURN: the new entry added in the list
+ */
+struct rip_info *rip_ecmp_replace(struct rip *rip, struct rip_info *rinfo_new)
+{
+ struct route_node *rp = rinfo_new->rp;
+ struct list *list = (struct list *)rp->info;
+ struct rip_info *rinfo = NULL, *tmp_rinfo = NULL;
+ struct listnode *node = NULL, *nextnode = NULL;
+
+ if (list == NULL || listcount(list) == 0)
+ return rip_ecmp_add(rip, rinfo_new);
+
+ /* Get the first entry */
+ rinfo = listgetdata(listhead(list));
+
+ /* Learnt route replaced by a local one. Delete it from zebra. */
+ if (rip_route_rte(rinfo) && !rip_route_rte(rinfo_new))
+ if (CHECK_FLAG(rinfo->flags, RIP_RTF_FIB))
+ rip_zebra_ipv4_delete(rip, rp);
+
+ /* Re-use the first entry, and delete the others. */
+ for (ALL_LIST_ELEMENTS(list, node, nextnode, tmp_rinfo)) {
+ if (tmp_rinfo == rinfo)
+ continue;
+
+ EVENT_OFF(tmp_rinfo->t_timeout);
+ EVENT_OFF(tmp_rinfo->t_garbage_collect);
+ list_delete_node(list, node);
+ rip_info_free(tmp_rinfo);
+ }
+
+ EVENT_OFF(rinfo->t_timeout);
+ EVENT_OFF(rinfo->t_garbage_collect);
+ memcpy(rinfo, rinfo_new, sizeof(struct rip_info));
+
+ if (rip_route_rte(rinfo)) {
+ rip_timeout_update(rip, rinfo);
+ /* The ADD message implies an update. */
+ rip_zebra_ipv4_add(rip, rp);
+ }
+
+ /* Set the route change flag. */
+ SET_FLAG(rinfo->flags, RIP_RTF_CHANGED);
+
+ /* Signal the output process to trigger an update (see section 2.5). */
+ rip_event(rip, RIP_TRIGGERED_UPDATE, 0);
+
+ return rinfo;
+}
+
+/* Delete one route from the ECMP list.
+ * RETURN:
+ * null - the entry is freed, and other entries exist in the list
+ * the entry - the entry is the last one in the list; its metric is set
+ * to INFINITY, and the garbage collector is started for it
+ */
+struct rip_info *rip_ecmp_delete(struct rip *rip, struct rip_info *rinfo)
+{
+ struct route_node *rp = rinfo->rp;
+ struct list *list = (struct list *)rp->info;
+
+ EVENT_OFF(rinfo->t_timeout);
+
+ if (listcount(list) > 1) {
+ /* Some other ECMP entries still exist. Just delete this entry.
+ */
+ EVENT_OFF(rinfo->t_garbage_collect);
+ listnode_delete(list, rinfo);
+ if (rip_route_rte(rinfo)
+ && CHECK_FLAG(rinfo->flags, RIP_RTF_FIB))
+ /* The ADD message implies the update. */
+ rip_zebra_ipv4_add(rip, rp);
+ rip_info_free(rinfo);
+ rinfo = NULL;
+ } else {
+ assert(rinfo == listgetdata(listhead(list)));
+
+ /* This is the only entry left in the list. We must keep it in
+ * the list for garbage collection time, with INFINITY metric.
+ */
+
+ rinfo->metric = RIP_METRIC_INFINITY;
+ RIP_TIMER_ON(rinfo->t_garbage_collect, rip_garbage_collect,
+ rip->garbage_time);
+
+ if (rip_route_rte(rinfo)
+ && CHECK_FLAG(rinfo->flags, RIP_RTF_FIB))
+ rip_zebra_ipv4_delete(rip, rp);
+ }
+
+ /* Set the route change flag on the first entry. */
+ rinfo = listgetdata(listhead(list));
+ SET_FLAG(rinfo->flags, RIP_RTF_CHANGED);
+
+ /* Signal the output process to trigger an update (see section 2.5). */
+ rip_event(rip, RIP_TRIGGERED_UPDATE, 0);
+
+ return rinfo;
+}
+
+/* Timeout RIP routes. */
+static void rip_timeout(struct event *t)
+{
+ struct rip_info *rinfo = EVENT_ARG(t);
+ struct rip *rip = rip_info_get_instance(rinfo);
+
+ rip_ecmp_delete(rip, rinfo);
+}
+
+static void rip_timeout_update(struct rip *rip, struct rip_info *rinfo)
+{
+ if (rinfo->metric != RIP_METRIC_INFINITY) {
+ EVENT_OFF(rinfo->t_timeout);
+ event_add_timer(master, rip_timeout, rinfo, rip->timeout_time,
+ &rinfo->t_timeout);
+ }
+}
+
+static int rip_filter(int rip_distribute, struct prefix_ipv4 *p,
+ struct rip_interface *ri)
+{
+ struct distribute *dist;
+ struct access_list *alist;
+ struct prefix_list *plist;
+ int distribute = rip_distribute == RIP_FILTER_OUT ? DISTRIBUTE_V4_OUT
+ : DISTRIBUTE_V4_IN;
+ const char *inout = rip_distribute == RIP_FILTER_OUT ? "out" : "in";
+
+ /* Input distribute-list filtering. */
+ if (ri->list[rip_distribute] &&
+ access_list_apply(ri->list[rip_distribute], (struct prefix *)p) ==
+ FILTER_DENY) {
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_debug("%pFX filtered by distribute %s", p, inout);
+ return -1;
+ }
+
+ if (ri->prefix[rip_distribute] &&
+ prefix_list_apply(ri->prefix[rip_distribute], (struct prefix *)p) ==
+ PREFIX_DENY) {
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_debug("%pFX filtered by prefix-list %s", p, inout);
+ return -1;
+ }
+
+ /* All interface filter check. */
+ dist = distribute_lookup(ri->rip->distribute_ctx, NULL);
+ if (!dist)
+ return 0;
+
+ if (dist->list[distribute]) {
+ alist = access_list_lookup(AFI_IP, dist->list[distribute]);
+
+ if (alist) {
+ if (access_list_apply(alist, (struct prefix *)p) ==
+ FILTER_DENY) {
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_debug(
+ "%pFX filtered by distribute %s",
+ p, inout);
+ return -1;
+ }
+ }
+ }
+ if (dist->prefix[distribute]) {
+ plist = prefix_list_lookup(AFI_IP, dist->prefix[distribute]);
+
+ if (plist) {
+ if (prefix_list_apply(plist, (struct prefix *)p) ==
+ PREFIX_DENY) {
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_debug(
+ "%pFX filtered by prefix-list %s",
+ p, inout);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Check nexthop address validity. */
+static int rip_nexthop_check(struct rip *rip, struct in_addr *addr)
+{
+ struct interface *ifp;
+ struct listnode *cnode;
+ struct connected *ifc;
+ struct prefix *p;
+
+ /* If nexthop address matches local configured address then it is
+ invalid nexthop. */
+
+ FOR_ALL_INTERFACES (rip->vrf, ifp) {
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, ifc)) {
+ p = ifc->address;
+
+ if (p->family == AF_INET
+ && IPV4_ADDR_SAME(&p->u.prefix4, addr))
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/* RIP add route to routing table. */
+static void rip_rte_process(struct rte *rte, struct sockaddr_in *from,
+ struct interface *ifp)
+{
+ struct rip *rip;
+ int ret;
+ struct prefix_ipv4 p;
+ struct route_node *rp;
+ struct rip_info *rinfo = NULL, newinfo;
+ struct rip_interface *ri;
+ struct in_addr *nexthop;
+ int same = 0;
+ unsigned char old_dist, new_dist;
+ struct list *list = NULL;
+ struct listnode *node = NULL;
+
+ /* Make prefix structure. */
+ memset(&p, 0, sizeof(struct prefix_ipv4));
+ p.family = AF_INET;
+ p.prefix = rte->prefix;
+ p.prefixlen = ip_masklen(rte->mask);
+
+ /* Make sure mask is applied. */
+ apply_mask_ipv4(&p);
+
+ ri = ifp->info;
+ rip = ri->rip;
+
+ /* Apply input filters. */
+ ret = rip_filter(RIP_FILTER_IN, &p, ri);
+ if (ret < 0)
+ return;
+
+ memset(&newinfo, 0, sizeof(newinfo));
+ newinfo.type = ZEBRA_ROUTE_RIP;
+ newinfo.sub_type = RIP_ROUTE_RTE;
+ newinfo.nh.gate.ipv4 = rte->nexthop;
+ newinfo.from = from->sin_addr;
+ newinfo.nh.ifindex = ifp->ifindex;
+ newinfo.nh.type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ newinfo.metric = rte->metric;
+ newinfo.metric_out = rte->metric; /* XXX */
+ newinfo.tag = ntohs(rte->tag); /* XXX */
+
+ /* Modify entry according to the interface routemap. */
+ if (ri->routemap[RIP_FILTER_IN]) {
+ /* The object should be of the type of rip_info */
+ ret = route_map_apply(ri->routemap[RIP_FILTER_IN],
+ (struct prefix *)&p, &newinfo);
+
+ if (ret == RMAP_DENYMATCH) {
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_debug(
+ "RIP %pFX is filtered by route-map in",
+ &p);
+ return;
+ }
+
+ /* Get back the object */
+ rte->nexthop = newinfo.nexthop_out;
+ rte->tag = htons(newinfo.tag_out); /* XXX */
+ rte->metric = newinfo.metric_out; /* XXX: the routemap uses the
+ metric_out field */
+ }
+
+ /* Once the entry has been validated, update the metric by
+ adding the cost of the network on which the message
+ arrived. If the result is greater than infinity, use infinity
+ (RFC2453 Sec. 3.9.2) */
+ /* Zebra ripd can handle offset-list in. */
+ ret = rip_offset_list_apply_in(&p, ifp, &rte->metric);
+
+ /* If offset-list does not modify the metric use interface's
+ metric. */
+ if (!ret)
+ rte->metric += ifp->metric ? ifp->metric : 1;
+
+ if (rte->metric > RIP_METRIC_INFINITY)
+ rte->metric = RIP_METRIC_INFINITY;
+
+ /* Set nexthop pointer. */
+ if (rte->nexthop.s_addr == INADDR_ANY)
+ nexthop = &from->sin_addr;
+ else
+ nexthop = &rte->nexthop;
+
+ /* Check if nexthop address is myself, then do nothing. */
+ if (rip_nexthop_check(rip, nexthop) < 0) {
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_debug("Nexthop address %pI4 is myself",
+ nexthop);
+ return;
+ }
+
+ /* Get index for the prefix. */
+ rp = route_node_get(rip->table, (struct prefix *)&p);
+
+ newinfo.rp = rp;
+ newinfo.nh.gate.ipv4 = *nexthop;
+ newinfo.nh.type = NEXTHOP_TYPE_IPV4;
+ newinfo.metric = rte->metric;
+ newinfo.tag = ntohs(rte->tag);
+ newinfo.distance = rip_distance_apply(rip, &newinfo);
+
+ new_dist = newinfo.distance ? newinfo.distance
+ : ZEBRA_RIP_DISTANCE_DEFAULT;
+
+ /* Check to see whether there is already RIP route on the table. */
+ if ((list = rp->info) != NULL)
+ for (ALL_LIST_ELEMENTS_RO(list, node, rinfo)) {
+ /* Need to compare with redistributed entry or local
+ * entry */
+ if (!rip_route_rte(rinfo))
+ break;
+
+ if (IPV4_ADDR_SAME(&rinfo->from, &from->sin_addr)
+ && IPV4_ADDR_SAME(&rinfo->nh.gate.ipv4, nexthop))
+ break;
+
+ if (listnextnode(node))
+ continue;
+
+ /* Not found in the list */
+
+ if (rte->metric > rinfo->metric) {
+ /* New route has a greater metric.
+ * Discard it. */
+ route_unlock_node(rp);
+ return;
+ }
+
+ if (rte->metric < rinfo->metric)
+ /* New route has a smaller metric.
+ * Replace the ECMP list
+ * with the new one in below. */
+ break;
+
+ /* Metrics are same. We compare the distances.
+ */
+ old_dist = rinfo->distance ? rinfo->distance
+ : ZEBRA_RIP_DISTANCE_DEFAULT;
+
+ if (new_dist > old_dist) {
+ /* New route has a greater distance.
+ * Discard it. */
+ route_unlock_node(rp);
+ return;
+ }
+
+ if (new_dist < old_dist)
+ /* New route has a smaller distance.
+ * Replace the ECMP list
+ * with the new one in below. */
+ break;
+
+ /* Metrics and distances are both same. Keep
+ * "rinfo" null and
+ * the new route is added in the ECMP list in
+ * below. */
+ }
+
+ if (rinfo) {
+ /* Local static route. */
+ if (rinfo->type == ZEBRA_ROUTE_RIP
+ && ((rinfo->sub_type == RIP_ROUTE_STATIC)
+ || (rinfo->sub_type == RIP_ROUTE_DEFAULT))
+ && rinfo->metric != RIP_METRIC_INFINITY) {
+ route_unlock_node(rp);
+ return;
+ }
+
+ /* Redistributed route check. */
+ if (rinfo->type != ZEBRA_ROUTE_RIP
+ && rinfo->metric != RIP_METRIC_INFINITY) {
+ old_dist = rinfo->distance;
+ /* Only routes directly connected to an interface
+ * (nexthop == 0)
+ * may have a valid NULL distance */
+ if (rinfo->nh.gate.ipv4.s_addr != INADDR_ANY)
+ old_dist = old_dist
+ ? old_dist
+ : ZEBRA_RIP_DISTANCE_DEFAULT;
+ /* If imported route does not have STRICT precedence,
+ mark it as a ghost */
+ if (new_dist <= old_dist
+ && rte->metric != RIP_METRIC_INFINITY)
+ rip_ecmp_replace(rip, &newinfo);
+
+ route_unlock_node(rp);
+ return;
+ }
+ }
+
+ if (!rinfo) {
+ if (rp->info)
+ route_unlock_node(rp);
+
+ /* Now, check to see whether there is already an explicit route
+ for the destination prefix. If there is no such route, add
+ this route to the routing table, unless the metric is
+ infinity (there is no point in adding a route which
+ unusable). */
+ if (rte->metric != RIP_METRIC_INFINITY)
+ rip_ecmp_add(rip, &newinfo);
+ } else {
+ /* Route is there but we are not sure the route is RIP or not.
+ */
+
+ /* If there is an existing route, compare the next hop address
+ to the address of the router from which the datagram came.
+ If this datagram is from the same router as the existing
+ route, reinitialize the timeout. */
+ same = (IPV4_ADDR_SAME(&rinfo->from, &from->sin_addr)
+ && (rinfo->nh.ifindex == ifp->ifindex));
+
+ old_dist = rinfo->distance ? rinfo->distance
+ : ZEBRA_RIP_DISTANCE_DEFAULT;
+
+ /* Next, compare the metrics. If the datagram is from the same
+ router as the existing route, and the new metric is different
+ than the old one; or, if the new metric is lower than the old
+ one, or if the tag has been changed; or if there is a route
+ with a lower administrave distance; or an update of the
+ distance on the actual route; do the following actions: */
+ if ((same && rinfo->metric != rte->metric)
+ || (rte->metric < rinfo->metric)
+ || ((same) && (rinfo->metric == rte->metric)
+ && (newinfo.tag != rinfo->tag))
+ || (old_dist > new_dist)
+ || ((old_dist != new_dist) && same)) {
+ if (listcount(list) == 1) {
+ if (newinfo.metric != RIP_METRIC_INFINITY)
+ rip_ecmp_replace(rip, &newinfo);
+ else
+ rip_ecmp_delete(rip, rinfo);
+ } else {
+ if (newinfo.metric < rinfo->metric)
+ rip_ecmp_replace(rip, &newinfo);
+ else if (newinfo.metric > rinfo->metric)
+ rip_ecmp_delete(rip, rinfo);
+ else if (new_dist < old_dist)
+ rip_ecmp_replace(rip, &newinfo);
+ else if (new_dist > old_dist)
+ rip_ecmp_delete(rip, rinfo);
+ else {
+ int update = CHECK_FLAG(rinfo->flags,
+ RIP_RTF_FIB)
+ ? 1
+ : 0;
+
+ assert(newinfo.metric
+ != RIP_METRIC_INFINITY);
+
+ EVENT_OFF(rinfo->t_timeout);
+ EVENT_OFF(rinfo->t_garbage_collect);
+ memcpy(rinfo, &newinfo,
+ sizeof(struct rip_info));
+ rip_timeout_update(rip, rinfo);
+
+ if (update)
+ rip_zebra_ipv4_add(rip, rp);
+
+ /* - Set the route change flag on the
+ * first entry. */
+ rinfo = listgetdata(listhead(list));
+ SET_FLAG(rinfo->flags, RIP_RTF_CHANGED);
+ rip_event(rip, RIP_TRIGGERED_UPDATE, 0);
+ }
+ }
+ } else /* same & no change */
+ rip_timeout_update(rip, rinfo);
+
+ /* Unlock tempolary lock of the route. */
+ route_unlock_node(rp);
+ }
+}
+
+/* Dump RIP packet */
+static void rip_packet_dump(struct rip_packet *packet, int size,
+ const char *sndrcv)
+{
+ caddr_t lim;
+ struct rte *rte;
+ const char *command_str;
+ uint8_t netmask = 0;
+ uint8_t *p;
+
+ /* Set command string. */
+ if (packet->command > 0 && packet->command < RIP_COMMAND_MAX)
+ command_str = lookup_msg(rip_msg, packet->command, NULL);
+ else
+ command_str = "unknown";
+
+ /* Dump packet header. */
+ zlog_debug("%s %s version %d packet size %d", sndrcv, command_str,
+ packet->version, size);
+
+ /* Dump each routing table entry. */
+ rte = packet->rte;
+
+ for (lim = (caddr_t)packet + size; (caddr_t)rte < lim; rte++) {
+ if (packet->version == RIPv2) {
+ netmask = ip_masklen(rte->mask);
+
+ if (rte->family == htons(RIP_FAMILY_AUTH)) {
+ if (rte->tag
+ == htons(RIP_AUTH_SIMPLE_PASSWORD)) {
+ p = (uint8_t *)&rte->prefix;
+
+ zlog_debug(
+ " family 0x%X type %d auth string: %s",
+ ntohs(rte->family),
+ ntohs(rte->tag), p);
+ } else if (rte->tag == htons(RIP_AUTH_MD5)) {
+ struct rip_md5_info *md5;
+
+ md5 = (struct rip_md5_info *)&packet
+ ->rte;
+
+ zlog_debug(
+ " family 0x%X type %d (MD5 authentication)",
+ ntohs(md5->family),
+ ntohs(md5->type));
+ zlog_debug(
+ " RIP-2 packet len %d Key ID %d Auth Data len %d",
+ ntohs(md5->packet_len),
+ md5->keyid, md5->auth_len);
+ zlog_debug(" Sequence Number %ld",
+ (unsigned long)ntohl(
+ md5->sequence));
+ } else if (rte->tag == htons(RIP_AUTH_DATA)) {
+ p = (uint8_t *)&rte->prefix;
+
+ zlog_debug(
+ " family 0x%X type %d (MD5 data)",
+ ntohs(rte->family),
+ ntohs(rte->tag));
+ zlog_debug(
+ " MD5: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
+ p[0], p[1], p[2], p[3], p[4],
+ p[5], p[6], p[7], p[8], p[9],
+ p[10], p[11], p[12], p[13],
+ p[14], p[15]);
+ } else {
+ zlog_debug(
+ " family 0x%X type %d (Unknown auth type)",
+ ntohs(rte->family),
+ ntohs(rte->tag));
+ }
+ } else
+ zlog_debug(
+ " %pI4/%d -> %pI4 family %d tag %" ROUTE_TAG_PRI
+ " metric %ld",
+ &rte->prefix, netmask, &rte->nexthop,
+ ntohs(rte->family),
+ (route_tag_t)ntohs(rte->tag),
+ (unsigned long)ntohl(rte->metric));
+ } else {
+ zlog_debug(" %pI4 family %d tag %" ROUTE_TAG_PRI
+ " metric %ld",
+ &rte->prefix, ntohs(rte->family),
+ (route_tag_t)ntohs(rte->tag),
+ (unsigned long)ntohl(rte->metric));
+ }
+ }
+}
+
+/* Check if the destination address is valid (unicast; not net 0
+ or 127) (RFC2453 Section 3.9.2 - Page 26). But we don't
+ check net 0 because we accept default route. */
+static int rip_destination_check(struct in_addr addr)
+{
+ uint32_t destination;
+
+ /* Convert to host byte order. */
+ destination = ntohl(addr.s_addr);
+
+ if (IPV4_NET127(destination))
+ return 0;
+
+ /* Net 0 may match to the default route. */
+ if (IPV4_NET0(destination) && destination != 0)
+ return 0;
+
+ /* Unicast address must belong to class A, B, C. */
+ if (IN_CLASSA(destination))
+ return 1;
+ if (IN_CLASSB(destination))
+ return 1;
+ if (IN_CLASSC(destination))
+ return 1;
+
+ return 0;
+}
+
+/* RIP version 2 authentication. */
+static int rip_auth_simple_password(struct rte *rte, struct sockaddr_in *from,
+ struct interface *ifp)
+{
+ struct rip_interface *ri;
+ char *auth_str = (char *)rte + offsetof(struct rte, prefix);
+ int i;
+
+ /* reject passwords with zeros in the middle of the string */
+ for (i = strnlen(auth_str, 16); i < 16; i++) {
+ if (auth_str[i] != '\0')
+ return 0;
+ }
+
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("RIPv2 simple password authentication from %pI4",
+ &from->sin_addr);
+
+ ri = ifp->info;
+
+ if (ri->auth_type != RIP_AUTH_SIMPLE_PASSWORD
+ || rte->tag != htons(RIP_AUTH_SIMPLE_PASSWORD))
+ return 0;
+
+ /* Simple password authentication. */
+ if (ri->auth_str) {
+ if (strncmp(auth_str, ri->auth_str, 16) == 0)
+ return 1;
+ }
+ if (ri->key_chain) {
+ struct keychain *keychain;
+ struct key *key;
+
+ keychain = keychain_lookup(ri->key_chain);
+ if (keychain == NULL || keychain->key == NULL)
+ return 0;
+
+ key = key_match_for_accept(keychain, auth_str);
+ if (key)
+ return 1;
+ }
+ return 0;
+}
+
+/* RIP version 2 authentication with MD5. */
+static int rip_auth_md5(struct rip_packet *packet, struct sockaddr_in *from,
+ int length, struct interface *ifp)
+{
+ struct rip_interface *ri;
+ struct rip_md5_info *md5;
+ struct rip_md5_data *md5data;
+ struct keychain *keychain;
+ struct key *key;
+#ifdef CRYPTO_OPENSSL
+ EVP_MD_CTX *ctx;
+#elif CRYPTO_INTERNAL
+ MD5_CTX ctx;
+#endif
+ uint8_t digest[RIP_AUTH_MD5_SIZE];
+ uint16_t packet_len;
+ char auth_str[RIP_AUTH_MD5_SIZE] = {};
+
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("RIPv2 MD5 authentication from %pI4",
+ &from->sin_addr);
+
+ ri = ifp->info;
+ md5 = (struct rip_md5_info *)&packet->rte;
+
+ /* Check auth type. */
+ if (ri->auth_type != RIP_AUTH_MD5 || md5->type != htons(RIP_AUTH_MD5))
+ return 0;
+
+ /* If the authentication length is less than 16, then it must be wrong
+ * for
+ * any interpretation of rfc2082. Some implementations also interpret
+ * this as RIP_HEADER_SIZE+ RIP_AUTH_MD5_SIZE, aka
+ * RIP_AUTH_MD5_COMPAT_SIZE.
+ */
+ if (!((md5->auth_len == RIP_AUTH_MD5_SIZE)
+ || (md5->auth_len == RIP_AUTH_MD5_COMPAT_SIZE))) {
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug(
+ "RIPv2 MD5 authentication, strange authentication length field %d",
+ md5->auth_len);
+ return 0;
+ }
+
+ /* grab and verify check packet length */
+ packet_len = ntohs(md5->packet_len);
+
+ if (packet_len > (length - RIP_HEADER_SIZE - RIP_AUTH_MD5_SIZE)) {
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug(
+ "RIPv2 MD5 authentication, packet length field %d greater than received length %d!",
+ md5->packet_len, length);
+ return 0;
+ }
+
+ /* retrieve authentication data */
+ md5data = (struct rip_md5_data *)(((uint8_t *)packet) + packet_len);
+
+ if (ri->key_chain) {
+ keychain = keychain_lookup(ri->key_chain);
+ if (keychain == NULL)
+ return 0;
+
+ key = key_lookup_for_accept(keychain, md5->keyid);
+ if (key == NULL || key->string == NULL)
+ return 0;
+
+ memcpy(auth_str, key->string,
+ MIN(sizeof(auth_str), strlen(key->string)));
+ } else if (ri->auth_str)
+ memcpy(auth_str, ri->auth_str,
+ MIN(sizeof(auth_str), strlen(ri->auth_str)));
+
+ if (auth_str[0] == 0)
+ return 0;
+
+ /* MD5 digest authentication. */
+#ifdef CRYPTO_OPENSSL
+ unsigned int md5_size = RIP_AUTH_MD5_SIZE;
+ ctx = EVP_MD_CTX_new();
+ EVP_DigestInit(ctx, EVP_md5());
+ EVP_DigestUpdate(ctx, packet, packet_len + RIP_HEADER_SIZE);
+ EVP_DigestUpdate(ctx, auth_str, RIP_AUTH_MD5_SIZE);
+ EVP_DigestFinal(ctx, digest, &md5_size);
+ EVP_MD_CTX_free(ctx);
+#elif CRYPTO_INTERNAL
+ memset(&ctx, 0, sizeof(ctx));
+ MD5Init(&ctx);
+ MD5Update(&ctx, packet, packet_len + RIP_HEADER_SIZE);
+ MD5Update(&ctx, auth_str, RIP_AUTH_MD5_SIZE);
+ MD5Final(digest, &ctx);
+#endif
+
+ if (memcmp(md5data->digest, digest, RIP_AUTH_MD5_SIZE) == 0)
+ return packet_len;
+ else
+ return 0;
+}
+
+/* Pick correct auth string for sends, prepare auth_str buffer for use.
+ * (left justified and padded).
+ *
+ * presumes one of ri or key is valid, and that the auth strings they point
+ * to are nul terminated. If neither are present, auth_str will be fully
+ * zero padded.
+ *
+ */
+static void rip_auth_prepare_str_send(struct rip_interface *ri, struct key *key,
+ char *auth_str, int len)
+{
+ assert(ri || key);
+
+ memset(auth_str, 0, len);
+ if (key && key->string)
+ memcpy(auth_str, key->string,
+ MIN((size_t)len, strlen(key->string)));
+ else if (ri->auth_str)
+ memcpy(auth_str, ri->auth_str,
+ MIN((size_t)len, strlen(ri->auth_str)));
+
+ return;
+}
+
+/* Write RIPv2 simple password authentication information
+ *
+ * auth_str is presumed to be 2 bytes and correctly prepared
+ * (left justified and zero padded).
+ */
+static void rip_auth_simple_write(struct stream *s, char *auth_str, int len)
+{
+ assert(s && len == RIP_AUTH_SIMPLE_SIZE);
+
+ stream_putw(s, RIP_FAMILY_AUTH);
+ stream_putw(s, RIP_AUTH_SIMPLE_PASSWORD);
+ stream_put(s, auth_str, RIP_AUTH_SIMPLE_SIZE);
+
+ return;
+}
+
+/* write RIPv2 MD5 "authentication header"
+ * (uses the auth key data field)
+ *
+ * Digest offset field is set to 0.
+ *
+ * returns: offset of the digest offset field, which must be set when
+ * length to the auth-data MD5 digest is known.
+ */
+static size_t rip_auth_md5_ah_write(struct stream *s, struct rip_interface *ri,
+ struct key *key)
+{
+ size_t doff = 0;
+ static uint32_t seq = 0;
+
+ assert(s && ri && ri->auth_type == RIP_AUTH_MD5);
+
+ /* MD5 authentication. */
+ stream_putw(s, RIP_FAMILY_AUTH);
+ stream_putw(s, RIP_AUTH_MD5);
+
+ /* MD5 AH digest offset field.
+ *
+ * Set to placeholder value here, to true value when RIP-2 Packet length
+ * is known. Actual value is set in .....().
+ */
+ doff = stream_get_endp(s);
+ stream_putw(s, 0);
+
+ /* Key ID. */
+ if (key)
+ stream_putc(s, key->index % 256);
+ else
+ stream_putc(s, 1);
+
+ /* Auth Data Len. Set 16 for MD5 authentication data. Older ripds
+ * however expect RIP_HEADER_SIZE + RIP_AUTH_MD5_SIZE so we allow for
+ * this
+ * to be configurable.
+ */
+ stream_putc(s, ri->md5_auth_len);
+
+ /* Sequence Number (non-decreasing). */
+ /* RFC2080: The value used in the sequence number is
+ arbitrary, but two suggestions are the time of the
+ message's creation or a simple message counter. */
+ stream_putl(s, ++seq);
+
+ /* Reserved field must be zero. */
+ stream_putl(s, 0);
+ stream_putl(s, 0);
+
+ return doff;
+}
+
+/* If authentication is in used, write the appropriate header
+ * returns stream offset to which length must later be written
+ * or 0 if this is not required
+ */
+static size_t rip_auth_header_write(struct stream *s, struct rip_interface *ri,
+ struct key *key, char *auth_str, int len)
+{
+ assert(ri->auth_type != RIP_NO_AUTH);
+
+ switch (ri->auth_type) {
+ case RIP_AUTH_SIMPLE_PASSWORD:
+ rip_auth_prepare_str_send(ri, key, auth_str, len);
+ rip_auth_simple_write(s, auth_str, len);
+ return 0;
+ case RIP_AUTH_MD5:
+ return rip_auth_md5_ah_write(s, ri, key);
+ }
+ assert(1);
+ return 0;
+}
+
+/* Write RIPv2 MD5 authentication data trailer */
+static void rip_auth_md5_set(struct stream *s, struct rip_interface *ri,
+ size_t doff, char *auth_str, int authlen)
+{
+ unsigned long len;
+#ifdef CRYPTO_OPENSSL
+ EVP_MD_CTX *ctx;
+#elif CRYPTO_INTERNAL
+ MD5_CTX ctx;
+#endif
+ unsigned char digest[RIP_AUTH_MD5_SIZE];
+
+ /* Make it sure this interface is configured as MD5
+ authentication. */
+ assert((ri->auth_type == RIP_AUTH_MD5)
+ && (authlen == RIP_AUTH_MD5_SIZE));
+ assert(doff > 0);
+
+ /* Get packet length. */
+ len = stream_get_endp(s);
+
+ /* Check packet length. */
+ if (len < (RIP_HEADER_SIZE + RIP_RTE_SIZE)) {
+ flog_err(EC_RIP_PACKET,
+ "%s: packet length %ld is less than minimum length.",
+ __func__, len);
+ return;
+ }
+
+ /* Set the digest offset length in the header */
+ stream_putw_at(s, doff, len);
+
+ /* Set authentication data. */
+ stream_putw(s, RIP_FAMILY_AUTH);
+ stream_putw(s, RIP_AUTH_DATA);
+
+ /* Generate a digest for the RIP packet. */
+#ifdef CRYPTO_OPENSSL
+ unsigned int md5_size = RIP_AUTH_MD5_SIZE;
+ ctx = EVP_MD_CTX_new();
+ EVP_DigestInit(ctx, EVP_md5());
+ EVP_DigestUpdate(ctx, STREAM_DATA(s), stream_get_endp(s));
+ EVP_DigestUpdate(ctx, auth_str, RIP_AUTH_MD5_SIZE);
+ EVP_DigestFinal(ctx, digest, &md5_size);
+ EVP_MD_CTX_free(ctx);
+#elif CRYPTO_INTERNAL
+ memset(&ctx, 0, sizeof(ctx));
+ MD5Init(&ctx);
+ MD5Update(&ctx, STREAM_DATA(s), stream_get_endp(s));
+ MD5Update(&ctx, auth_str, RIP_AUTH_MD5_SIZE);
+ MD5Final(digest, &ctx);
+#endif
+
+ /* Copy the digest to the packet. */
+ stream_write(s, digest, RIP_AUTH_MD5_SIZE);
+}
+
+/* RIP routing information. */
+static void rip_response_process(struct rip_packet *packet, int size,
+ struct sockaddr_in *from,
+ struct connected *ifc)
+{
+ struct rip_interface *ri = ifc->ifp->info;
+ struct rip *rip = ri->rip;
+ caddr_t lim;
+ struct rte *rte;
+ struct prefix_ipv4 ifaddr;
+ struct prefix_ipv4 ifaddrclass;
+ int subnetted;
+
+ memset(&ifaddr, 0, sizeof(ifaddr));
+ /* We don't know yet. */
+ subnetted = -1;
+
+ /* The Response must be ignored if it is not from the RIP
+ port. (RFC2453 - Sec. 3.9.2)*/
+ if (from->sin_port != htons(RIP_PORT_DEFAULT)) {
+ zlog_info("response doesn't come from RIP port: %d",
+ from->sin_port);
+ rip_peer_bad_packet(rip, ri, from);
+ return;
+ }
+
+ /* The datagram's IPv4 source address should be checked to see
+ whether the datagram is from a valid neighbor; the source of the
+ datagram must be on a directly connected network (RFC2453 - Sec.
+ 3.9.2) */
+ if (if_lookup_address((void *)&from->sin_addr, AF_INET,
+ rip->vrf->vrf_id)
+ == NULL) {
+ zlog_info(
+ "This datagram doesn't come from a valid neighbor: %pI4",
+ &from->sin_addr);
+ rip_peer_bad_packet(rip, ri, from);
+ return;
+ }
+
+ /* It is also worth checking to see whether the response is from one
+ of the router's own addresses. */
+
+ ; /* Alredy done in rip_read () */
+
+ /* Update RIP peer. */
+ rip_peer_update(rip, ri, from, packet->version);
+
+ /* Set RTE pointer. */
+ rte = packet->rte;
+
+ for (lim = (caddr_t)packet + size; (caddr_t)rte < lim; rte++) {
+ /* RIPv2 authentication check. */
+ /* If the Address Family Identifier of the first (and only the
+ first) entry in the message is 0xFFFF, then the remainder of
+ the entry contains the authentication. */
+ /* If the packet gets here it means authentication enabled */
+ /* Check is done in rip_read(). So, just skipping it */
+ if (packet->version == RIPv2 && rte == packet->rte
+ && rte->family == htons(RIP_FAMILY_AUTH))
+ continue;
+
+ if (rte->family != htons(AF_INET)) {
+ /* Address family check. RIP only supports AF_INET. */
+ zlog_info("Unsupported family %d from %pI4",
+ ntohs(rte->family),
+ &from->sin_addr);
+ continue;
+ }
+
+ if (packet->version == RIPv1 && rte->tag != 0) {
+ zlog_warn("RIPv1 reserved field is nonzero: %d",
+ ntohs(rte->tag));
+ continue;
+ }
+
+ /* - is the destination address valid (e.g., unicast; not net 0
+ or 127) */
+ if (!rip_destination_check(rte->prefix)) {
+ zlog_info(
+ "Network is net 0 or net 127 or it is not unicast network");
+ rip_peer_bad_route(rip, ri, from);
+ continue;
+ }
+
+ /* Convert metric value to host byte order. */
+ rte->metric = ntohl(rte->metric);
+
+ /* - is the metric valid (i.e., between 1 and 16, inclusive) */
+ if (!(rte->metric >= 1 && rte->metric <= 16)) {
+ zlog_info("Route's metric is not in the 1-16 range.");
+ rip_peer_bad_route(rip, ri, from);
+ continue;
+ }
+
+ /* RIPv1 does not have nexthop value. */
+ if (packet->version == RIPv1
+ && rte->nexthop.s_addr != INADDR_ANY) {
+ zlog_info("RIPv1 packet with nexthop value %pI4",
+ &rte->nexthop);
+ rip_peer_bad_route(rip, ri, from);
+ continue;
+ }
+
+ /* That is, if the provided information is ignored, a possibly
+ sub-optimal, but absolutely valid, route may be taken. If
+ the received Next Hop is not directly reachable, it should be
+ treated as 0.0.0.0. */
+ if (packet->version == RIPv2
+ && rte->nexthop.s_addr != INADDR_ANY) {
+ uint32_t addrval;
+
+ /* Multicast address check. */
+ addrval = ntohl(rte->nexthop.s_addr);
+ if (IN_CLASSD(addrval)) {
+ zlog_info(
+ "Nexthop %pI4 is multicast address, skip this rte",
+ &rte->nexthop);
+ continue;
+ }
+
+ if (!if_lookup_address((void *)&rte->nexthop, AF_INET,
+ rip->vrf->vrf_id)) {
+ struct route_node *rn;
+ struct rip_info *rinfo;
+
+ rn = route_node_match_ipv4(rip->table,
+ &rte->nexthop);
+
+ if (rn) {
+ rinfo = rn->info;
+
+ if (rinfo->type == ZEBRA_ROUTE_RIP
+ && rinfo->sub_type
+ == RIP_ROUTE_RTE) {
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug(
+ "Next hop %pI4 is on RIP network. Set nexthop to the packet's originator",
+ &rte->nexthop);
+ rte->nexthop = rinfo->from;
+ } else {
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug(
+ "Next hop %pI4 is not directly reachable. Treat it as 0.0.0.0",
+ &rte->nexthop);
+ rte->nexthop.s_addr =
+ INADDR_ANY;
+ }
+
+ route_unlock_node(rn);
+ } else {
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug(
+ "Next hop %pI4 is not directly reachable. Treat it as 0.0.0.0",
+ &rte->nexthop);
+ rte->nexthop.s_addr = INADDR_ANY;
+ }
+ }
+ }
+
+ /* For RIPv1, there won't be a valid netmask.
+ * This is a best guess at the masks. If everyone was using old
+ * Ciscos before the 'ip subnet zero' option, it would be almost
+ * right too :-)
+ *
+ * Cisco summarize ripv1 advertisements to the classful boundary
+ * (/16 for class B's) except when the RIP packet does to inside
+ * the classful network in question.
+ */
+ if ((packet->version == RIPv1
+ && rte->prefix.s_addr != INADDR_ANY)
+ || (packet->version == RIPv2
+ && (rte->prefix.s_addr != INADDR_ANY
+ && rte->mask.s_addr == INADDR_ANY))) {
+ uint32_t destination;
+
+ if (subnetted == -1) {
+ memcpy(&ifaddr, ifc->address, sizeof(ifaddr));
+ memcpy(&ifaddrclass, &ifaddr,
+ sizeof(ifaddrclass));
+ apply_classful_mask_ipv4(&ifaddrclass);
+ subnetted = 0;
+ if (ifaddr.prefixlen > ifaddrclass.prefixlen)
+ subnetted = 1;
+ }
+
+ destination = ntohl(rte->prefix.s_addr);
+
+ if (IN_CLASSA(destination))
+ masklen2ip(8, &rte->mask);
+ else if (IN_CLASSB(destination))
+ masklen2ip(16, &rte->mask);
+ else if (IN_CLASSC(destination))
+ masklen2ip(24, &rte->mask);
+
+ if (subnetted == 1)
+ masklen2ip(ifaddrclass.prefixlen,
+ (struct in_addr *)&destination);
+ if ((subnetted == 1)
+ && ((rte->prefix.s_addr & destination)
+ == ifaddrclass.prefix.s_addr)) {
+ masklen2ip(ifaddr.prefixlen, &rte->mask);
+ if ((rte->prefix.s_addr & rte->mask.s_addr)
+ != rte->prefix.s_addr)
+ masklen2ip(32, &rte->mask);
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("Subnetted route %pI4",
+ &rte->prefix);
+ } else {
+ if ((rte->prefix.s_addr & rte->mask.s_addr)
+ != rte->prefix.s_addr)
+ continue;
+ }
+
+ if (IS_RIP_DEBUG_EVENT) {
+ zlog_debug("Resultant route %pI4",
+ &rte->prefix);
+ zlog_debug("Resultant mask %pI4",
+ &rte->mask);
+ }
+ }
+
+ /* In case of RIPv2, if prefix in RTE is not netmask applied one
+ ignore the entry. */
+ if ((packet->version == RIPv2)
+ && (rte->mask.s_addr != INADDR_ANY)
+ && ((rte->prefix.s_addr & rte->mask.s_addr)
+ != rte->prefix.s_addr)) {
+ zlog_warn(
+ "RIPv2 address %pI4 is not mask /%d applied one",
+ &rte->prefix, ip_masklen(rte->mask));
+ rip_peer_bad_route(rip, ri, from);
+ continue;
+ }
+
+ /* Default route's netmask is ignored. */
+ if (packet->version == RIPv2
+ && (rte->prefix.s_addr == INADDR_ANY)
+ && (rte->mask.s_addr != INADDR_ANY)) {
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug(
+ "Default route with non-zero netmask. Set zero to netmask");
+ rte->mask.s_addr = INADDR_ANY;
+ }
+
+ /* Routing table updates. */
+ rip_rte_process(rte, from, ifc->ifp);
+ }
+}
+
+/* Make socket for RIP protocol. */
+int rip_create_socket(struct vrf *vrf)
+{
+ int ret;
+ int sock;
+ struct sockaddr_in addr;
+ const char *vrf_dev = NULL;
+
+ memset(&addr, 0, sizeof(struct sockaddr_in));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ addr.sin_len = sizeof(struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+ /* sending port must always be the RIP port */
+ addr.sin_port = htons(RIP_PORT_DEFAULT);
+
+ /* Make datagram socket. */
+ if (vrf->vrf_id != VRF_DEFAULT)
+ vrf_dev = vrf->name;
+ frr_with_privs(&ripd_privs) {
+ sock = vrf_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, vrf->vrf_id,
+ vrf_dev);
+ if (sock < 0) {
+ flog_err_sys(EC_LIB_SOCKET,
+ "Cannot create UDP socket: %s",
+ safe_strerror(errno));
+ return -1;
+ }
+ }
+
+ sockopt_broadcast(sock);
+ sockopt_reuseaddr(sock);
+ sockopt_reuseport(sock);
+ setsockopt_ipv4_multicast_loop(sock, 0);
+#ifdef IPTOS_PREC_INTERNETCONTROL
+ setsockopt_ipv4_tos(sock, IPTOS_PREC_INTERNETCONTROL);
+#endif
+ setsockopt_so_recvbuf(sock, RIP_UDP_RCV_BUF);
+
+ frr_with_privs(&ripd_privs) {
+ if ((ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr)))
+ < 0) {
+ zlog_err("%s: Can't bind socket %d to %pI4 port %d: %s",
+ __func__, sock, &addr.sin_addr,
+ (int)ntohs(addr.sin_port),
+ safe_strerror(errno));
+
+ close(sock);
+ return ret;
+ }
+ }
+
+ return sock;
+}
+
+/* RIP packet send to destination address, on interface denoted by
+ * by connected argument. NULL to argument denotes destination should be
+ * should be RIP multicast group
+ */
+static int rip_send_packet(uint8_t *buf, int size, struct sockaddr_in *to,
+ struct connected *ifc)
+{
+ struct rip_interface *ri;
+ struct rip *rip;
+ int ret;
+ struct sockaddr_in sin;
+ struct msghdr msg;
+ struct iovec iov;
+#ifdef GNU_LINUX
+ struct cmsghdr *cmsgptr;
+ char adata[256] = {};
+ struct in_pktinfo *pkt;
+#endif /* GNU_LINUX */
+
+ assert(ifc != NULL);
+ ri = ifc->ifp->info;
+ rip = ri->rip;
+
+ if (IS_RIP_DEBUG_PACKET) {
+#define ADDRESS_SIZE 20
+ char dst[ADDRESS_SIZE];
+
+ if (to) {
+ inet_ntop(AF_INET, &to->sin_addr, dst, sizeof(dst));
+ } else {
+ sin.sin_addr.s_addr = htonl(INADDR_RIP_GROUP);
+ inet_ntop(AF_INET, &sin.sin_addr, dst, sizeof(dst));
+ }
+#undef ADDRESS_SIZE
+ zlog_debug("%s %pI4 > %s (%s)", __func__,
+ &ifc->address->u.prefix4, dst, ifc->ifp->name);
+ }
+
+ if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY)) {
+ /*
+ * ZEBRA_IFA_SECONDARY is set on linux when an interface is
+ * configured with multiple addresses on the same
+ * subnet: the first address on the subnet is configured
+ * "primary", and all subsequent addresses on that subnet
+ * are treated as "secondary" addresses. In order to avoid
+ * routing-table bloat on other rip listeners, we do not send
+ * out RIP packets with ZEBRA_IFA_SECONDARY source addrs.
+ * XXX Since Linux is the only system for which the
+ * ZEBRA_IFA_SECONDARY flag is set, we would end up
+ * sending a packet for a "secondary" source address on
+ * non-linux systems.
+ */
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_debug("duplicate dropped");
+ return 0;
+ }
+
+ /* Make destination address. */
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ sin.sin_len = sizeof(struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+
+ /* When destination is specified, use it's port and address. */
+ if (to) {
+ sin.sin_port = to->sin_port;
+ sin.sin_addr = to->sin_addr;
+ } else {
+ sin.sin_port = htons(RIP_PORT_DEFAULT);
+ sin.sin_addr.s_addr = htonl(INADDR_RIP_GROUP);
+
+ rip_interface_multicast_set(rip->sock, ifc);
+ }
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = (void *)&sin;
+ msg.msg_namelen = sizeof(struct sockaddr_in);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ iov.iov_base = buf;
+ iov.iov_len = size;
+
+#ifdef GNU_LINUX
+ msg.msg_control = (void *)adata;
+ msg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
+
+ cmsgptr = (struct cmsghdr *)adata;
+ cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+ cmsgptr->cmsg_level = IPPROTO_IP;
+ cmsgptr->cmsg_type = IP_PKTINFO;
+ pkt = (struct in_pktinfo *)CMSG_DATA(cmsgptr);
+ pkt->ipi_ifindex = ifc->ifp->ifindex;
+ pkt->ipi_spec_dst.s_addr = ifc->address->u.prefix4.s_addr;
+#endif /* GNU_LINUX */
+
+ ret = sendmsg(rip->sock, &msg, 0);
+
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("SEND to %pI4 port %d", &sin.sin_addr,
+ ntohs(sin.sin_port));
+
+ if (ret < 0)
+ zlog_warn("can't send packet : %s", safe_strerror(errno));
+
+ return ret;
+}
+
+/* Add redistributed route to RIP table. */
+void rip_redistribute_add(struct rip *rip, int type, int sub_type,
+ struct prefix_ipv4 *p, struct nexthop *nh,
+ unsigned int metric, unsigned char distance,
+ route_tag_t tag)
+{
+ int ret;
+ struct route_node *rp = NULL;
+ struct rip_info *rinfo = NULL, newinfo;
+ struct list *list = NULL;
+
+ /* Redistribute route */
+ ret = rip_destination_check(p->prefix);
+ if (!ret)
+ return;
+
+ rp = route_node_get(rip->table, (struct prefix *)p);
+
+ memset(&newinfo, 0, sizeof(newinfo));
+ newinfo.type = type;
+ newinfo.sub_type = sub_type;
+ newinfo.metric = 1;
+ newinfo.external_metric = metric;
+ newinfo.distance = distance;
+ if (tag <= UINT16_MAX) /* RIP only supports 16 bit tags */
+ newinfo.tag = tag;
+ newinfo.rp = rp;
+ newinfo.nh = *nh;
+
+ if ((list = rp->info) != NULL && listcount(list) != 0) {
+ rinfo = listgetdata(listhead(list));
+
+ if (rinfo->type == ZEBRA_ROUTE_CONNECT
+ && rinfo->sub_type == RIP_ROUTE_INTERFACE
+ && rinfo->metric != RIP_METRIC_INFINITY) {
+ route_unlock_node(rp);
+ return;
+ }
+
+ /* Manually configured RIP route check. */
+ if (rinfo->type == ZEBRA_ROUTE_RIP
+ && ((rinfo->sub_type == RIP_ROUTE_STATIC)
+ || (rinfo->sub_type == RIP_ROUTE_DEFAULT))) {
+ if (type != ZEBRA_ROUTE_RIP
+ || ((sub_type != RIP_ROUTE_STATIC)
+ && (sub_type != RIP_ROUTE_DEFAULT))) {
+ route_unlock_node(rp);
+ return;
+ }
+ }
+
+ (void)rip_ecmp_replace(rip, &newinfo);
+ route_unlock_node(rp);
+ } else
+ (void)rip_ecmp_add(rip, &newinfo);
+
+ if (IS_RIP_DEBUG_EVENT) {
+ zlog_debug("Redistribute new prefix %pFX", p);
+ }
+
+ rip_event(rip, RIP_TRIGGERED_UPDATE, 0);
+}
+
+/* Delete redistributed route from RIP table. */
+void rip_redistribute_delete(struct rip *rip, int type, int sub_type,
+ struct prefix_ipv4 *p, ifindex_t ifindex)
+{
+ int ret;
+ struct route_node *rp;
+ struct rip_info *rinfo;
+
+ ret = rip_destination_check(p->prefix);
+ if (!ret)
+ return;
+
+ rp = route_node_lookup(rip->table, (struct prefix *)p);
+ if (rp) {
+ struct list *list = rp->info;
+
+ if (list != NULL && listcount(list) != 0) {
+ rinfo = listgetdata(listhead(list));
+ if (rinfo != NULL && rinfo->type == type
+ && rinfo->sub_type == sub_type
+ && rinfo->nh.ifindex == ifindex) {
+ /* Perform poisoned reverse. */
+ rinfo->metric = RIP_METRIC_INFINITY;
+ RIP_TIMER_ON(rinfo->t_garbage_collect,
+ rip_garbage_collect,
+ rip->garbage_time);
+ EVENT_OFF(rinfo->t_timeout);
+ rinfo->flags |= RIP_RTF_CHANGED;
+
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug(
+ "Poison %pFX on the interface %s with an infinity metric [delete]",
+ p,
+ ifindex2ifname(
+ ifindex,
+ rip->vrf->vrf_id));
+
+ rip_event(rip, RIP_TRIGGERED_UPDATE, 0);
+ }
+ }
+ route_unlock_node(rp);
+ }
+}
+
+/* Response to request called from rip_read ().*/
+static void rip_request_process(struct rip_packet *packet, int size,
+ struct sockaddr_in *from, struct connected *ifc)
+{
+ struct rip *rip;
+ caddr_t lim;
+ struct rte *rte;
+ struct prefix_ipv4 p;
+ struct route_node *rp;
+ struct rip_info *rinfo;
+ struct rip_interface *ri;
+
+ /* Does not reponse to the requests on the loopback interfaces */
+ if (if_is_loopback(ifc->ifp))
+ return;
+
+ /* Check RIP process is enabled on this interface. */
+ ri = ifc->ifp->info;
+ if (!ri->running)
+ return;
+ rip = ri->rip;
+
+ /* When passive interface is specified, suppress responses */
+ if (ri->passive)
+ return;
+
+ /* RIP peer update. */
+ rip_peer_update(rip, ri, from, packet->version);
+
+ lim = ((caddr_t)packet) + size;
+ rte = packet->rte;
+
+ /* The Request is processed entry by entry. If there are no
+ entries, no response is given. */
+ if (lim == (caddr_t)rte)
+ return;
+
+ /* There is one special case. If there is exactly one entry in the
+ request, and it has an address family identifier of zero and a
+ metric of infinity (i.e., 16), then this is a request to send the
+ entire routing table. */
+ if (lim == ((caddr_t)(rte + 1)) && ntohs(rte->family) == 0
+ && ntohl(rte->metric) == RIP_METRIC_INFINITY) {
+ /* All route with split horizon */
+ rip_output_process(ifc, from, rip_all_route, packet->version);
+ } else {
+ if (ntohs(rte->family) != AF_INET)
+ return;
+
+ /* Examine the list of RTEs in the Request one by one. For each
+ entry, look up the destination in the router's routing
+ database and, if there is a route, put that route's metric in
+ the metric field of the RTE. If there is no explicit route
+ to the specified destination, put infinity in the metric
+ field. Once all the entries have been filled in, change the
+ command from Request to Response and send the datagram back
+ to the requestor. */
+ p.family = AF_INET;
+
+ for (; ((caddr_t)rte) < lim; rte++) {
+ p.prefix = rte->prefix;
+ p.prefixlen = ip_masklen(rte->mask);
+ apply_mask_ipv4(&p);
+
+ rp = route_node_lookup(rip->table, (struct prefix *)&p);
+ if (rp) {
+ rinfo = listgetdata(
+ listhead((struct list *)rp->info));
+ rte->metric = htonl(rinfo->metric);
+ route_unlock_node(rp);
+ } else
+ rte->metric = htonl(RIP_METRIC_INFINITY);
+ }
+ packet->command = RIP_RESPONSE;
+
+ (void)rip_send_packet((uint8_t *)packet, size, from, ifc);
+ }
+ rip->counters.queries++;
+}
+
+/* First entry point of RIP packet. */
+static void rip_read(struct event *t)
+{
+ struct rip *rip = EVENT_ARG(t);
+ int sock;
+ int ret;
+ int rtenum;
+ union rip_buf rip_buf;
+ struct rip_packet *packet;
+ struct sockaddr_in from;
+ int len;
+ int vrecv;
+ socklen_t fromlen;
+ struct interface *ifp = NULL;
+ struct connected *ifc;
+ struct rip_interface *ri = NULL;
+ struct prefix p;
+
+ /* Fetch socket then register myself. */
+ sock = EVENT_FD(t);
+
+ /* Add myself to tne next event */
+ rip_event(rip, RIP_READ, sock);
+
+ /* RIPd manages only IPv4. */
+ memset(&from, 0, sizeof(from));
+ fromlen = sizeof(struct sockaddr_in);
+
+ len = recvfrom(sock, (char *)&rip_buf.buf, sizeof(rip_buf.buf), 0,
+ (struct sockaddr *)&from, &fromlen);
+ if (len < 0) {
+ zlog_info("recvfrom failed (VRF %s): %s", rip->vrf_name,
+ safe_strerror(errno));
+ return;
+ }
+
+ /* Check is this packet comming from myself? */
+ if (if_check_address(rip, from.sin_addr)) {
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_debug("ignore packet comes from myself (VRF %s)",
+ rip->vrf_name);
+ return;
+ }
+
+ /* Which interface is this packet comes from. */
+ ifc = if_lookup_address((void *)&from.sin_addr, AF_INET,
+ rip->vrf->vrf_id);
+ if (ifc) {
+ ifp = ifc->ifp;
+ ri = ifp->info;
+ }
+
+ /* RIP packet received */
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("RECV packet from %pI4 port %d on %s (VRF %s)",
+ &from.sin_addr, ntohs(from.sin_port),
+ ifp ? ifp->name : "unknown", rip->vrf_name);
+
+ /* If this packet come from unknown interface, ignore it. */
+ if (ifp == NULL || ri == NULL) {
+ zlog_info(
+ "%s: cannot find interface for packet from %pI4 port %d (VRF %s)",
+ __func__, &from.sin_addr, ntohs(from.sin_port),
+ rip->vrf_name);
+ return;
+ }
+
+ p.family = AF_INET;
+ p.u.prefix4 = from.sin_addr;
+ p.prefixlen = IPV4_MAX_BITLEN;
+
+ ifc = connected_lookup_prefix(ifp, &p);
+
+ if (ifc == NULL) {
+ zlog_info(
+ "%s: cannot find connected address for packet from %pI4 port %d on interface %s (VRF %s)",
+ __func__, &from.sin_addr, ntohs(from.sin_port),
+ ifp->name, rip->vrf_name);
+ return;
+ }
+
+ /* Packet length check. */
+ if (len < RIP_PACKET_MINSIZ) {
+ zlog_warn("packet size %d is smaller than minimum size %d", len,
+ RIP_PACKET_MINSIZ);
+ rip_peer_bad_packet(rip, ri, &from);
+ return;
+ }
+ if (len > RIP_PACKET_MAXSIZ) {
+ zlog_warn("packet size %d is larger than max size %d", len,
+ RIP_PACKET_MAXSIZ);
+ rip_peer_bad_packet(rip, ri, &from);
+ return;
+ }
+
+ /* Packet alignment check. */
+ if ((len - RIP_PACKET_MINSIZ) % 20) {
+ zlog_warn("packet size %d is wrong for RIP packet alignment",
+ len);
+ rip_peer_bad_packet(rip, ri, &from);
+ return;
+ }
+
+ /* Set RTE number. */
+ rtenum = ((len - RIP_PACKET_MINSIZ) / 20);
+
+ /* For easy to handle. */
+ packet = &rip_buf.rip_packet;
+
+ /* RIP version check. */
+ if (packet->version == 0) {
+ zlog_info("version 0 with command %d received.",
+ packet->command);
+ rip_peer_bad_packet(rip, ri, &from);
+ return;
+ }
+
+ /* Dump RIP packet. */
+ if (IS_RIP_DEBUG_RECV)
+ rip_packet_dump(packet, len, "RECV");
+
+ /* RIP version adjust. This code should rethink now. RFC1058 says
+ that "Version 1 implementations are to ignore this extra data and
+ process only the fields specified in this document.". So RIPv3
+ packet should be treated as RIPv1 ignoring must be zero field. */
+ if (packet->version > RIPv2)
+ packet->version = RIPv2;
+
+ /* Is RIP running or is this RIP neighbor ?*/
+ if (!ri->running && !rip_neighbor_lookup(rip, &from)) {
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("RIP is not enabled on interface %s.",
+ ifp->name);
+ rip_peer_bad_packet(rip, ri, &from);
+ return;
+ }
+
+ /* RIP Version check. RFC2453, 4.6 and 5.1 */
+ vrecv = ((ri->ri_receive == RI_RIP_UNSPEC) ? rip->version_recv
+ : ri->ri_receive);
+ if (vrecv == RI_RIP_VERSION_NONE
+ || ((packet->version == RIPv1) && !(vrecv & RIPv1))
+ || ((packet->version == RIPv2) && !(vrecv & RIPv2))) {
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_debug(
+ " packet's v%d doesn't fit to if version spec",
+ packet->version);
+ rip_peer_bad_packet(rip, ri, &from);
+ return;
+ }
+
+ /* RFC2453 5.2 If the router is not configured to authenticate RIP-2
+ messages, then RIP-1 and unauthenticated RIP-2 messages will be
+ accepted; authenticated RIP-2 messages shall be discarded. */
+ if ((ri->auth_type == RIP_NO_AUTH) && rtenum
+ && (packet->version == RIPv2)
+ && (packet->rte->family == htons(RIP_FAMILY_AUTH))) {
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug(
+ "packet RIPv%d is dropped because authentication disabled",
+ packet->version);
+ ripd_notif_send_auth_type_failure(ifp->name);
+ rip_peer_bad_packet(rip, ri, &from);
+ return;
+ }
+
+ /* RFC:
+ If the router is configured to authenticate RIP-2 messages, then
+ RIP-1 messages and RIP-2 messages which pass authentication
+ testing shall be accepted; unauthenticated and failed
+ authentication RIP-2 messages shall be discarded. For maximum
+ security, RIP-1 messages should be ignored when authentication is
+ in use (see section 4.1); otherwise, the routing information from
+ authenticated messages will be propagated by RIP-1 routers in an
+ unauthenticated manner.
+ */
+ /* We make an exception for RIPv1 REQUEST packets, to which we'll
+ * always reply regardless of authentication settings, because:
+ *
+ * - if there other authorised routers on-link, the REQUESTor can
+ * passively obtain the routing updates anyway
+ * - if there are no other authorised routers on-link, RIP can
+ * easily be disabled for the link to prevent giving out information
+ * on state of this routers RIP routing table..
+ *
+ * I.e. if RIPv1 has any place anymore these days, it's as a very
+ * simple way to distribute routing information (e.g. to embedded
+ * hosts / appliances) and the ability to give out RIPv1
+ * routing-information freely, while still requiring RIPv2
+ * authentication for any RESPONSEs might be vaguely useful.
+ */
+ if (ri->auth_type != RIP_NO_AUTH && packet->version == RIPv1) {
+ /* Discard RIPv1 messages other than REQUESTs */
+ if (packet->command != RIP_REQUEST) {
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_debug(
+ "RIPv1 dropped because authentication enabled");
+ ripd_notif_send_auth_type_failure(ifp->name);
+ rip_peer_bad_packet(rip, ri, &from);
+ return;
+ }
+ } else if (ri->auth_type != RIP_NO_AUTH) {
+ const char *auth_desc;
+
+ if (rtenum == 0) {
+ /* There definitely is no authentication in the packet.
+ */
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_debug(
+ "RIPv2 authentication failed: no auth RTE in packet");
+ ripd_notif_send_auth_type_failure(ifp->name);
+ rip_peer_bad_packet(rip, ri, &from);
+ return;
+ }
+
+ /* First RTE must be an Authentication Family RTE */
+ if (packet->rte->family != htons(RIP_FAMILY_AUTH)) {
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_debug(
+ "RIPv2 dropped because authentication enabled");
+ ripd_notif_send_auth_type_failure(ifp->name);
+ rip_peer_bad_packet(rip, ri, &from);
+ return;
+ }
+
+ /* Check RIPv2 authentication. */
+ switch (ntohs(packet->rte->tag)) {
+ case RIP_AUTH_SIMPLE_PASSWORD:
+ auth_desc = "simple";
+ ret = rip_auth_simple_password(packet->rte, &from, ifp);
+ break;
+
+ case RIP_AUTH_MD5:
+ auth_desc = "MD5";
+ ret = rip_auth_md5(packet, &from, len, ifp);
+ /* Reset RIP packet length to trim MD5 data. */
+ len = ret;
+ break;
+
+ default:
+ ret = 0;
+ auth_desc = "unknown type";
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_debug(
+ "RIPv2 Unknown authentication type %d",
+ ntohs(packet->rte->tag));
+ }
+
+ if (ret) {
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_debug("RIPv2 %s authentication success",
+ auth_desc);
+ } else {
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_debug("RIPv2 %s authentication failure",
+ auth_desc);
+ ripd_notif_send_auth_failure(ifp->name);
+ rip_peer_bad_packet(rip, ri, &from);
+ return;
+ }
+ }
+
+ /* Process each command. */
+ switch (packet->command) {
+ case RIP_RESPONSE:
+ rip_response_process(packet, len, &from, ifc);
+ break;
+ case RIP_REQUEST:
+ case RIP_POLL:
+ rip_request_process(packet, len, &from, ifc);
+ break;
+ case RIP_TRACEON:
+ case RIP_TRACEOFF:
+ zlog_info(
+ "Obsolete command %s received, please sent it to routed",
+ lookup_msg(rip_msg, packet->command, NULL));
+ rip_peer_bad_packet(rip, ri, &from);
+ break;
+ case RIP_POLL_ENTRY:
+ zlog_info("Obsolete command %s received",
+ lookup_msg(rip_msg, packet->command, NULL));
+ rip_peer_bad_packet(rip, ri, &from);
+ break;
+ default:
+ zlog_info("Unknown RIP command %d received", packet->command);
+ rip_peer_bad_packet(rip, ri, &from);
+ break;
+ }
+}
+
+/* Write routing table entry to the stream and return next index of
+ the routing table entry in the stream. */
+static int rip_write_rte(int num, struct stream *s, struct prefix_ipv4 *p,
+ uint8_t version, struct rip_info *rinfo)
+{
+ struct in_addr mask;
+
+ /* Write routing table entry. */
+ if (version == RIPv1) {
+ stream_putw(s, AF_INET);
+ stream_putw(s, 0);
+ stream_put_ipv4(s, p->prefix.s_addr);
+ stream_put_ipv4(s, 0);
+ stream_put_ipv4(s, 0);
+ stream_putl(s, rinfo->metric_out);
+ } else {
+ masklen2ip(p->prefixlen, &mask);
+
+ stream_putw(s, AF_INET);
+ stream_putw(s, rinfo->tag_out);
+ stream_put_ipv4(s, p->prefix.s_addr);
+ stream_put_ipv4(s, mask.s_addr);
+ stream_put_ipv4(s, rinfo->nexthop_out.s_addr);
+ stream_putl(s, rinfo->metric_out);
+ }
+
+ return ++num;
+}
+
+/* Send update to the ifp or spcified neighbor. */
+void rip_output_process(struct connected *ifc, struct sockaddr_in *to,
+ int route_type, uint8_t version)
+{
+ struct rip *rip;
+ int ret;
+ struct stream *s;
+ struct route_node *rp;
+ struct rip_info *rinfo;
+ struct rip_interface *ri;
+ struct prefix_ipv4 *p;
+ struct prefix_ipv4 classfull;
+ struct prefix_ipv4 ifaddrclass;
+ struct key *key = NULL;
+ /* this might need to made dynamic if RIP ever supported auth methods
+ with larger key string sizes */
+ char auth_str[RIP_AUTH_SIMPLE_SIZE];
+ size_t doff = 0; /* offset of digest offset field */
+ int num = 0;
+ int rtemax;
+ int subnetted = 0;
+ struct list *list = NULL;
+ struct listnode *listnode = NULL;
+
+ /* Logging output event. */
+ if (IS_RIP_DEBUG_EVENT) {
+ if (to)
+ zlog_debug("update routes to neighbor %pI4",
+ &to->sin_addr);
+ else
+ zlog_debug("update routes on interface %s ifindex %d",
+ ifc->ifp->name, ifc->ifp->ifindex);
+ }
+
+ /* Get RIP interface. */
+ ri = ifc->ifp->info;
+ rip = ri->rip;
+
+ /* Set output stream. */
+ s = rip->obuf;
+
+ /* Reset stream and RTE counter. */
+ stream_reset(s);
+ rtemax = RIP_MAX_RTE;
+
+ /* If output interface is in simple password authentication mode, we
+ need space for authentication data. */
+ if (ri->auth_type == RIP_AUTH_SIMPLE_PASSWORD)
+ rtemax -= 1;
+
+ /* If output interface is in MD5 authentication mode, we need space
+ for authentication header and data. */
+ if (ri->auth_type == RIP_AUTH_MD5)
+ rtemax -= 2;
+
+ /* If output interface is in simple password authentication mode
+ and string or keychain is specified we need space for auth. data */
+ if (ri->auth_type != RIP_NO_AUTH) {
+ if (ri->key_chain) {
+ struct keychain *keychain;
+
+ keychain = keychain_lookup(ri->key_chain);
+ if (keychain)
+ key = key_lookup_for_send(keychain);
+ }
+ /* to be passed to auth functions later */
+ rip_auth_prepare_str_send(ri, key, auth_str, sizeof(auth_str));
+ if (strlen(auth_str) == 0)
+ return;
+ }
+
+ if (version == RIPv1) {
+ memcpy(&ifaddrclass, ifc->address, sizeof(ifaddrclass));
+ apply_classful_mask_ipv4(&ifaddrclass);
+ subnetted = 0;
+ if (ifc->address->prefixlen > ifaddrclass.prefixlen)
+ subnetted = 1;
+ }
+
+ for (rp = route_top(rip->table); rp; rp = route_next(rp)) {
+ list = rp->info;
+
+ if (list == NULL)
+ continue;
+
+ if (listcount(list) == 0)
+ continue;
+
+ rinfo = listgetdata(listhead(list));
+ /*
+ * For RIPv1, if we are subnetted, output subnets in our
+ * network that have the same mask as the output "interface".
+ * For other networks, only the classfull version is output.
+ */
+ if (version == RIPv1) {
+ p = (struct prefix_ipv4 *)&rp->p;
+
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_debug(
+ "RIPv1 mask check, %pFX considered for output",
+ &rp->p);
+
+ if (subnetted &&
+ prefix_match((struct prefix *)&ifaddrclass,
+ &rp->p)) {
+ if ((ifc->address->prefixlen !=
+ rp->p.prefixlen) &&
+ (rp->p.prefixlen != IPV4_MAX_BITLEN))
+ continue;
+ } else {
+ memcpy(&classfull, &rp->p,
+ sizeof(struct prefix_ipv4));
+ apply_classful_mask_ipv4(&classfull);
+ if (rp->p.u.prefix4.s_addr != INADDR_ANY &&
+ classfull.prefixlen != rp->p.prefixlen)
+ continue;
+ }
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_debug(
+ "RIPv1 mask check, %pFX made it through",
+ &rp->p);
+ } else
+ p = (struct prefix_ipv4 *)&rp->p;
+
+ /* Apply output filters. */
+ ret = rip_filter(RIP_FILTER_OUT, p, ri);
+ if (ret < 0)
+ continue;
+
+ /* Changed route only output. */
+ if (route_type == rip_changed_route &&
+ (!(rinfo->flags & RIP_RTF_CHANGED)))
+ continue;
+
+ /* Split horizon. */
+ if (ri->split_horizon == RIP_SPLIT_HORIZON) {
+ /*
+ * We perform split horizon for RIP and connected
+ * route. For rip routes, we want to suppress the
+ * route if we would end up sending the route back on
+ * the interface that we learned it from, with a
+ * higher metric. For connected routes, we suppress
+ * the route if the prefix is a subset of the source
+ * address that we are going to use for the packet
+ * (in order to handle the case when multiple subnets
+ * are configured on the same interface).
+ */
+ int suppress = 0;
+ struct rip_info *tmp_rinfo = NULL;
+ struct connected *tmp_ifc = NULL;
+
+ for (ALL_LIST_ELEMENTS_RO(list, listnode, tmp_rinfo))
+ if (tmp_rinfo->type == ZEBRA_ROUTE_RIP &&
+ tmp_rinfo->nh.ifindex ==
+ ifc->ifp->ifindex) {
+ suppress = 1;
+ break;
+ }
+
+ if (!suppress && rinfo->type == ZEBRA_ROUTE_CONNECT) {
+ for (ALL_LIST_ELEMENTS_RO(ifc->ifp->connected,
+ listnode, tmp_ifc))
+ if (prefix_match((struct prefix *)p,
+ tmp_ifc->address)) {
+ suppress = 1;
+ break;
+ }
+ }
+
+ if (suppress)
+ continue;
+ }
+
+ /* Preparation for route-map. */
+ rinfo->metric_set = 0;
+ rinfo->nexthop_out.s_addr = 0;
+ rinfo->metric_out = rinfo->metric;
+ rinfo->tag_out = rinfo->tag;
+ rinfo->ifindex_out = ifc->ifp->ifindex;
+
+ /* In order to avoid some local loops, if the RIP route has
+ * a nexthop via this interface, keep the nexthop, otherwise
+ * set it to 0. The nexthop should not be propagated beyond
+ * the local broadcast/multicast area in order to avoid an
+ * IGP multi-level recursive look-up. see (4.4)
+ */
+ if (rinfo->nh.ifindex == ifc->ifp->ifindex)
+ rinfo->nexthop_out = rinfo->nh.gate.ipv4;
+
+ /* Interface route-map */
+ if (ri->routemap[RIP_FILTER_OUT]) {
+ ret = route_map_apply(ri->routemap[RIP_FILTER_OUT],
+ (struct prefix *)p, rinfo);
+
+ if (ret == RMAP_DENYMATCH) {
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_debug(
+ "RIP %pFX is filtered by route-map out",
+ p);
+ continue;
+ }
+ }
+
+ /* Apply redistribute route map - continue, if deny */
+ if (rip->redist[rinfo->type].route_map.name &&
+ rinfo->sub_type != RIP_ROUTE_INTERFACE) {
+ ret = route_map_apply(
+ rip->redist[rinfo->type].route_map.map,
+ (struct prefix *)p, rinfo);
+
+ if (ret == RMAP_DENYMATCH) {
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_debug(
+ "%pFX is filtered by route-map",
+ p);
+ continue;
+ }
+ }
+
+ /* When route-map does not set metric. */
+ if (!rinfo->metric_set) {
+ /* If redistribute metric is set. */
+ if (rip->redist[rinfo->type].metric_config &&
+ rinfo->metric != RIP_METRIC_INFINITY) {
+ rinfo->metric_out =
+ rip->redist[rinfo->type].metric;
+ } else {
+ /* If the route is not connected or localy
+ * generated one, use default-metric value
+ */
+ if (rinfo->type != ZEBRA_ROUTE_RIP &&
+ rinfo->type != ZEBRA_ROUTE_CONNECT &&
+ rinfo->metric != RIP_METRIC_INFINITY)
+ rinfo->metric_out = rip->default_metric;
+ }
+ }
+
+ /* Apply offset-list */
+ if (rinfo->metric != RIP_METRIC_INFINITY)
+ rip_offset_list_apply_out(p, ifc->ifp,
+ &rinfo->metric_out);
+
+ if (rinfo->metric_out > RIP_METRIC_INFINITY)
+ rinfo->metric_out = RIP_METRIC_INFINITY;
+
+ /* Perform split-horizon with poisoned reverse
+ * for RIP and connected routes.
+ **/
+ if (ri->split_horizon == RIP_SPLIT_HORIZON_POISONED_REVERSE) {
+ /*
+ * We perform split horizon for RIP and connected
+ * route. For rip routes, we want to suppress the
+ * route if we would end up sending the route back
+ * on the interface that we learned it from, with a
+ * higher metric. For connected routes, we suppress
+ * the route if the prefix is a subset of the source
+ * address that we are going to use for the packet
+ * (in order to handle the case when multiple
+ * subnets are configured on the same interface).
+ */
+ struct rip_info *tmp_rinfo = NULL;
+ struct connected *tmp_ifc = NULL;
+
+ for (ALL_LIST_ELEMENTS_RO(list, listnode, tmp_rinfo))
+ if (tmp_rinfo->type == ZEBRA_ROUTE_RIP &&
+ tmp_rinfo->nh.ifindex == ifc->ifp->ifindex)
+ rinfo->metric_out = RIP_METRIC_INFINITY;
+
+ if (rinfo->metric_out != RIP_METRIC_INFINITY &&
+ rinfo->type == ZEBRA_ROUTE_CONNECT) {
+ for (ALL_LIST_ELEMENTS_RO(ifc->ifp->connected,
+ listnode, tmp_ifc))
+ if (prefix_match((struct prefix *)p,
+ tmp_ifc->address)) {
+ rinfo->metric_out =
+ RIP_METRIC_INFINITY;
+ break;
+ }
+ }
+ }
+
+ /* Prepare preamble, auth headers, if needs be */
+ if (num == 0) {
+ stream_putc(s, RIP_RESPONSE);
+ stream_putc(s, version);
+ stream_putw(s, 0);
+
+ /* auth header for !v1 && !no_auth */
+ if ((ri->auth_type != RIP_NO_AUTH) &&
+ (version != RIPv1))
+ doff = rip_auth_header_write(
+ s, ri, key, auth_str,
+ RIP_AUTH_SIMPLE_SIZE);
+ }
+
+ /* Write RTE to the stream. */
+ num = rip_write_rte(num, s, p, version, rinfo);
+ if (num == rtemax) {
+ if (version == RIPv2 && ri->auth_type == RIP_AUTH_MD5)
+ rip_auth_md5_set(s, ri, doff, auth_str,
+ RIP_AUTH_SIMPLE_SIZE);
+
+ ret = rip_send_packet(STREAM_DATA(s),
+ stream_get_endp(s), to, ifc);
+
+ if (ret >= 0 && IS_RIP_DEBUG_SEND)
+ rip_packet_dump(
+ (struct rip_packet *)STREAM_DATA(s),
+ stream_get_endp(s), "SEND");
+ num = 0;
+ stream_reset(s);
+ }
+ }
+
+ /* Flush unwritten RTE. */
+ if (num != 0) {
+ if (version == RIPv2 && ri->auth_type == RIP_AUTH_MD5)
+ rip_auth_md5_set(s, ri, doff, auth_str,
+ RIP_AUTH_SIMPLE_SIZE);
+
+ ret = rip_send_packet(STREAM_DATA(s), stream_get_endp(s), to,
+ ifc);
+
+ if (ret >= 0 && IS_RIP_DEBUG_SEND)
+ rip_packet_dump((struct rip_packet *)STREAM_DATA(s),
+ stream_get_endp(s), "SEND");
+ stream_reset(s);
+ }
+
+ /* Statistics updates. */
+ ri->sent_updates++;
+}
+
+/* Send RIP packet to the interface. */
+static void rip_update_interface(struct connected *ifc, uint8_t version,
+ int route_type)
+{
+ struct interface *ifp = ifc->ifp;
+ struct rip_interface *ri = ifp->info;
+ struct sockaddr_in to;
+
+ /* When RIP version is 2 and multicast enable interface. */
+ if (version == RIPv2 && !ri->v2_broadcast && if_is_multicast(ifp)) {
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("multicast announce on %s ", ifp->name);
+
+ rip_output_process(ifc, NULL, route_type, version);
+ return;
+ }
+
+ /* If we can't send multicast packet, send it with unicast. */
+ if (if_is_broadcast(ifp) || if_is_pointopoint(ifp)) {
+ if (ifc->address->family == AF_INET) {
+ /* Destination address and port setting. */
+ memset(&to, 0, sizeof(to));
+ if (ifc->destination)
+ /* use specified broadcast or peer destination
+ * addr */
+ to.sin_addr = ifc->destination->u.prefix4;
+ else if (ifc->address->prefixlen < IPV4_MAX_BITLEN)
+ /* calculate the appropriate broadcast address
+ */
+ to.sin_addr.s_addr = ipv4_broadcast_addr(
+ ifc->address->u.prefix4.s_addr,
+ ifc->address->prefixlen);
+ else
+ /* do not know where to send the packet */
+ return;
+ to.sin_port = htons(RIP_PORT_DEFAULT);
+
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("%s announce to %pI4 on %s",
+ CONNECTED_PEER(ifc) ? "unicast"
+ : "broadcast",
+ &to.sin_addr, ifp->name);
+
+ rip_output_process(ifc, &to, route_type, version);
+ }
+ }
+}
+
+/* Update send to all interface and neighbor. */
+static void rip_update_process(struct rip *rip, int route_type)
+{
+ struct listnode *ifnode, *ifnnode;
+ struct connected *connected;
+ struct interface *ifp;
+ struct rip_interface *ri;
+ struct route_node *rp;
+ struct sockaddr_in to;
+ struct prefix *p;
+
+ /* Send RIP update to each interface. */
+ FOR_ALL_INTERFACES (rip->vrf, ifp) {
+ if (if_is_loopback(ifp))
+ continue;
+
+ if (!if_is_operative(ifp))
+ continue;
+
+ /* Fetch RIP interface information. */
+ ri = ifp->info;
+
+ /* When passive interface is specified, suppress announce to the
+ interface. */
+ if (ri->passive)
+ continue;
+
+ if (!ri->running)
+ continue;
+
+ /*
+ * If there is no version configuration in the
+ * interface, use rip's version setting.
+ */
+ int vsend = ((ri->ri_send == RI_RIP_UNSPEC) ? rip->version_send
+ : ri->ri_send);
+
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("SEND UPDATE to %s ifindex %d", ifp->name,
+ ifp->ifindex);
+
+ /* send update on each connected network */
+ for (ALL_LIST_ELEMENTS(ifp->connected, ifnode, ifnnode,
+ connected)) {
+ if (connected->address->family == AF_INET) {
+ if (vsend & RIPv1)
+ rip_update_interface(connected, RIPv1,
+ route_type);
+ if ((vsend & RIPv2) && if_is_multicast(ifp))
+ rip_update_interface(connected, RIPv2,
+ route_type);
+ }
+ }
+ }
+
+ /* RIP send updates to each neighbor. */
+ for (rp = route_top(rip->neighbor); rp; rp = route_next(rp)) {
+ if (rp->info == NULL)
+ continue;
+
+ p = &rp->p;
+
+ connected = if_lookup_address(&p->u.prefix4, AF_INET,
+ rip->vrf->vrf_id);
+ if (!connected) {
+ zlog_warn(
+ "Neighbor %pI4 doesn't have connected interface!",
+ &p->u.prefix4);
+ continue;
+ }
+
+ /* Set destination address and port */
+ memset(&to, 0, sizeof(struct sockaddr_in));
+ to.sin_addr = p->u.prefix4;
+ to.sin_port = htons(RIP_PORT_DEFAULT);
+
+ /* RIP version is rip's configuration. */
+ rip_output_process(connected, &to, route_type,
+ rip->version_send);
+ }
+}
+
+/* RIP's periodical timer. */
+static void rip_update(struct event *t)
+{
+ struct rip *rip = EVENT_ARG(t);
+
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("update timer fire!");
+
+ /* Process update output. */
+ rip_update_process(rip, rip_all_route);
+
+ /* Triggered updates may be suppressed if a regular update is due by
+ the time the triggered update would be sent. */
+ EVENT_OFF(rip->t_triggered_interval);
+ rip->trigger = 0;
+
+ /* Register myself. */
+ rip_event(rip, RIP_UPDATE_EVENT, 0);
+}
+
+/* Walk down the RIP routing table then clear changed flag. */
+static void rip_clear_changed_flag(struct rip *rip)
+{
+ struct route_node *rp;
+ struct rip_info *rinfo = NULL;
+ struct list *list = NULL;
+ struct listnode *listnode = NULL;
+
+ for (rp = route_top(rip->table); rp; rp = route_next(rp)) {
+ list = rp->info;
+
+ if (list == NULL)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) {
+ UNSET_FLAG(rinfo->flags, RIP_RTF_CHANGED);
+ /* This flag can be set only on the first entry. */
+ break;
+ }
+ }
+}
+
+/* Triggered update interval timer. */
+static void rip_triggered_interval(struct event *t)
+{
+ struct rip *rip = EVENT_ARG(t);
+
+ if (rip->trigger) {
+ rip->trigger = 0;
+ rip_triggered_update(t);
+ }
+}
+
+/* Execute triggered update. */
+static void rip_triggered_update(struct event *t)
+{
+ struct rip *rip = EVENT_ARG(t);
+ int interval;
+
+ /* Cancel interval timer. */
+ EVENT_OFF(rip->t_triggered_interval);
+ rip->trigger = 0;
+
+ /* Logging triggered update. */
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("triggered update!");
+
+ /* Split Horizon processing is done when generating triggered
+ updates as well as normal updates (see section 2.6). */
+ rip_update_process(rip, rip_changed_route);
+
+ /* Once all of the triggered updates have been generated, the route
+ change flags should be cleared. */
+ rip_clear_changed_flag(rip);
+
+ /* After a triggered update is sent, a timer should be set for a
+ random interval between 1 and 5 seconds. If other changes that
+ would trigger updates occur before the timer expires, a single
+ update is triggered when the timer expires. */
+ interval = (frr_weak_random() % 5) + 1;
+
+ event_add_timer(master, rip_triggered_interval, rip, interval,
+ &rip->t_triggered_interval);
+}
+
+/* Withdraw redistributed route. */
+void rip_redistribute_withdraw(struct rip *rip, int type)
+{
+ struct route_node *rp;
+ struct rip_info *rinfo = NULL;
+ struct list *list = NULL;
+
+ for (rp = route_top(rip->table); rp; rp = route_next(rp)) {
+ list = rp->info;
+
+ if (list == NULL)
+ continue;
+
+ rinfo = listgetdata(listhead(list));
+
+ if (rinfo->type != type)
+ continue;
+
+ if (rinfo->sub_type == RIP_ROUTE_INTERFACE)
+ continue;
+
+ /* Perform poisoned reverse. */
+ rinfo->metric = RIP_METRIC_INFINITY;
+ RIP_TIMER_ON(rinfo->t_garbage_collect, rip_garbage_collect,
+ rip->garbage_time);
+ EVENT_OFF(rinfo->t_timeout);
+ rinfo->flags |= RIP_RTF_CHANGED;
+
+ if (IS_RIP_DEBUG_EVENT) {
+ struct prefix_ipv4 *p = (struct prefix_ipv4 *)&rp->p;
+
+ zlog_debug(
+ "Poisone %pFX on the interface %s with an infinity metric [withdraw]",
+ p,
+ ifindex2ifname(rinfo->nh.ifindex,
+ rip->vrf->vrf_id));
+ }
+
+ rip_event(rip, RIP_TRIGGERED_UPDATE, 0);
+ }
+}
+
+struct rip *rip_lookup_by_vrf_id(vrf_id_t vrf_id)
+{
+ struct vrf *vrf;
+
+ vrf = vrf_lookup_by_id(vrf_id);
+ if (!vrf)
+ return NULL;
+
+ return vrf->info;
+}
+
+struct rip *rip_lookup_by_vrf_name(const char *vrf_name)
+{
+ struct rip rip;
+
+ rip.vrf_name = (char *)vrf_name;
+
+ return RB_FIND(rip_instance_head, &rip_instances, &rip);
+}
+
+/* Update ECMP routes to zebra when `allow-ecmp` changed. */
+void rip_ecmp_change(struct rip *rip)
+{
+ struct route_node *rp;
+ struct rip_info *rinfo;
+ struct list *list;
+ struct listnode *node, *nextnode;
+
+ for (rp = route_top(rip->table); rp; rp = route_next(rp)) {
+ list = rp->info;
+ if (list && listcount(list) > 1) {
+ while (listcount(list) > rip->ecmp) {
+ struct rip_info *from_highest = NULL;
+
+ for (ALL_LIST_ELEMENTS(list, node, nextnode,
+ rinfo)) {
+ if (!from_highest ||
+ (from_highest &&
+ IPV4_ADDR_CMP(
+ &rinfo->from,
+ &from_highest->from) > 0))
+ from_highest = rinfo;
+ }
+
+ rip_ecmp_delete(rip, from_highest);
+ }
+ }
+ }
+}
+
+/* Create new RIP instance and set it to global variable. */
+struct rip *rip_create(const char *vrf_name, struct vrf *vrf, int socket)
+{
+ struct rip *rip;
+
+ rip = XCALLOC(MTYPE_RIP, sizeof(struct rip));
+ rip->vrf_name = XSTRDUP(MTYPE_RIP_VRF_NAME, vrf_name);
+
+ /* Set initial value. */
+ rip->ecmp = yang_get_default_uint8("%s/allow-ecmp", RIP_INSTANCE);
+ rip->default_metric =
+ yang_get_default_uint8("%s/default-metric", RIP_INSTANCE);
+ rip->distance =
+ yang_get_default_uint8("%s/distance/default", RIP_INSTANCE);
+ rip->passive_default =
+ yang_get_default_bool("%s/passive-default", RIP_INSTANCE);
+ rip->garbage_time = yang_get_default_uint32("%s/timers/flush-interval",
+ RIP_INSTANCE);
+ rip->timeout_time = yang_get_default_uint32(
+ "%s/timers/holddown-interval", RIP_INSTANCE);
+ rip->update_time = yang_get_default_uint32("%s/timers/update-interval",
+ RIP_INSTANCE);
+ rip->version_send =
+ yang_get_default_enum("%s/version/send", RIP_INSTANCE);
+ rip->version_recv =
+ yang_get_default_enum("%s/version/receive", RIP_INSTANCE);
+
+ /* Initialize RIP data structures. */
+ rip->table = route_table_init();
+ route_table_set_info(rip->table, rip);
+ rip->neighbor = route_table_init();
+ rip->peer_list = list_new();
+ rip->peer_list->cmp = (int (*)(void *, void *))rip_peer_list_cmp;
+ rip->peer_list->del = rip_peer_list_del;
+ rip->distance_table = route_table_init();
+ rip->distance_table->cleanup = rip_distance_table_node_cleanup;
+ rip->enable_interface = vector_init(1);
+ rip->enable_network = route_table_init();
+ rip->passive_nondefault = vector_init(1);
+ rip->offset_list_master = list_new();
+ rip->offset_list_master->cmp = (int (*)(void *, void *))offset_list_cmp;
+ rip->offset_list_master->del = (void (*)(void *))offset_list_free;
+
+ /* Distribute list install. */
+ rip->distribute_ctx = distribute_list_ctx_create(vrf);
+ distribute_list_add_hook(rip->distribute_ctx, rip_distribute_update);
+ distribute_list_delete_hook(rip->distribute_ctx, rip_distribute_update);
+
+ /* if rmap install. */
+ rip->if_rmap_ctx = if_rmap_ctx_create(vrf_name);
+ if_rmap_hook_add(rip->if_rmap_ctx, rip_if_rmap_update);
+ if_rmap_hook_delete(rip->if_rmap_ctx, rip_if_rmap_update);
+
+ /* Make output stream. */
+ rip->obuf = stream_new(1500);
+
+ /* Enable the routing instance if possible. */
+ if (vrf && vrf_is_enabled(vrf))
+ rip_instance_enable(rip, vrf, socket);
+ else {
+ rip->vrf = NULL;
+ rip->sock = -1;
+ }
+
+ RB_INSERT(rip_instance_head, &rip_instances, rip);
+
+ return rip;
+}
+
+/* Sned RIP request to the destination. */
+int rip_request_send(struct sockaddr_in *to, struct interface *ifp,
+ uint8_t version, struct connected *connected)
+{
+ struct rte *rte;
+ struct rip_packet rip_packet;
+ struct listnode *node, *nnode;
+
+ memset(&rip_packet, 0, sizeof(rip_packet));
+
+ rip_packet.command = RIP_REQUEST;
+ rip_packet.version = version;
+ rte = rip_packet.rte;
+ rte->metric = htonl(RIP_METRIC_INFINITY);
+
+ if (connected) {
+ /*
+ * connected is only sent for ripv1 case, or when
+ * interface does not support multicast. Caller loops
+ * over each connected address for this case.
+ */
+ if (rip_send_packet((uint8_t *)&rip_packet, sizeof(rip_packet),
+ to, connected)
+ != sizeof(rip_packet))
+ return -1;
+ else
+ return sizeof(rip_packet);
+ }
+
+ /* send request on each connected network */
+ for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, connected)) {
+ struct prefix_ipv4 *p;
+
+ p = (struct prefix_ipv4 *)connected->address;
+
+ if (p->family != AF_INET)
+ continue;
+
+ if (rip_send_packet((uint8_t *)&rip_packet, sizeof(rip_packet),
+ to, connected)
+ != sizeof(rip_packet))
+ return -1;
+ }
+ return sizeof(rip_packet);
+}
+
+static int rip_update_jitter(unsigned long time)
+{
+#define JITTER_BOUND 4
+ /* We want to get the jitter to +/- 1/JITTER_BOUND the interval.
+ Given that, we cannot let time be less than JITTER_BOUND seconds.
+ The RIPv2 RFC says jitter should be small compared to
+ update_time. We consider 1/JITTER_BOUND to be small.
+ */
+
+ int jitter_input = time;
+ int jitter;
+
+ if (jitter_input < JITTER_BOUND)
+ jitter_input = JITTER_BOUND;
+
+ jitter = (((frr_weak_random() % ((jitter_input * 2) + 1))
+ - jitter_input));
+
+ return jitter / JITTER_BOUND;
+}
+
+void rip_event(struct rip *rip, enum rip_event event, int sock)
+{
+ int jitter = 0;
+
+ switch (event) {
+ case RIP_READ:
+ event_add_read(master, rip_read, rip, sock, &rip->t_read);
+ break;
+ case RIP_UPDATE_EVENT:
+ EVENT_OFF(rip->t_update);
+ jitter = rip_update_jitter(rip->update_time);
+ event_add_timer(master, rip_update, rip,
+ sock ? 2 : rip->update_time + jitter,
+ &rip->t_update);
+ break;
+ case RIP_TRIGGERED_UPDATE:
+ if (rip->t_triggered_interval)
+ rip->trigger = 1;
+ else
+ event_add_event(master, rip_triggered_update, rip, 0,
+ &rip->t_triggered_update);
+ break;
+ default:
+ break;
+ }
+}
+
+struct rip_distance *rip_distance_new(void)
+{
+ return XCALLOC(MTYPE_RIP_DISTANCE, sizeof(struct rip_distance));
+}
+
+void rip_distance_free(struct rip_distance *rdistance)
+{
+ if (rdistance->access_list)
+ free(rdistance->access_list);
+ XFREE(MTYPE_RIP_DISTANCE, rdistance);
+}
+
+static void rip_distance_table_node_cleanup(struct route_table *table,
+ struct route_node *node)
+{
+ struct rip_distance *rdistance;
+
+ rdistance = node->info;
+ if (rdistance)
+ rip_distance_free(rdistance);
+}
+
+/* Apply RIP information to distance method. */
+uint8_t rip_distance_apply(struct rip *rip, struct rip_info *rinfo)
+{
+ struct route_node *rn;
+ struct prefix_ipv4 p;
+ struct rip_distance *rdistance;
+ struct access_list *alist;
+
+ memset(&p, 0, sizeof(p));
+ p.family = AF_INET;
+ p.prefix = rinfo->from;
+ p.prefixlen = IPV4_MAX_BITLEN;
+
+ /* Check source address. */
+ rn = route_node_match(rip->distance_table, (struct prefix *)&p);
+ if (rn) {
+ rdistance = rn->info;
+ route_unlock_node(rn);
+
+ if (rdistance->access_list) {
+ alist = access_list_lookup(AFI_IP,
+ rdistance->access_list);
+ if (alist == NULL)
+ return 0;
+ if (access_list_apply(alist, &rinfo->rp->p)
+ == FILTER_DENY)
+ return 0;
+ }
+ return rdistance->distance;
+ }
+
+ return rip->distance;
+}
+
+static void rip_distance_show(struct vty *vty, struct rip *rip)
+{
+ struct route_node *rn;
+ struct rip_distance *rdistance;
+ int header = 1;
+ char buf[BUFSIZ];
+
+ vty_out(vty, " Distance: (default is %u)\n",
+ rip->distance ? rip->distance : ZEBRA_RIP_DISTANCE_DEFAULT);
+
+ for (rn = route_top(rip->distance_table); rn; rn = route_next(rn)) {
+ rdistance = rn->info;
+
+ if (rdistance == NULL)
+ continue;
+
+ if (header) {
+ vty_out(vty, " Address Distance List\n");
+ header = 0;
+ }
+ snprintfrr(buf, sizeof(buf), "%pFX", &rn->p);
+ vty_out(vty, " %-20s %4d %s\n", buf, rdistance->distance,
+ rdistance->access_list ? rdistance->access_list : "");
+ }
+}
+
+/* Update ECMP routes to zebra when ECMP is disabled. */
+void rip_ecmp_disable(struct rip *rip)
+{
+ struct route_node *rp;
+ struct rip_info *rinfo, *tmp_rinfo;
+ struct list *list;
+ struct listnode *node, *nextnode;
+
+ for (rp = route_top(rip->table); rp; rp = route_next(rp)) {
+ list = rp->info;
+
+ if (!list)
+ continue;
+ if (listcount(list) == 0)
+ continue;
+
+ rinfo = listgetdata(listhead(list));
+ if (!rip_route_rte(rinfo))
+ continue;
+
+ /* Drop all other entries, except the first one. */
+ for (ALL_LIST_ELEMENTS(list, node, nextnode, tmp_rinfo)) {
+ if (tmp_rinfo == rinfo)
+ continue;
+
+ EVENT_OFF(tmp_rinfo->t_timeout);
+ EVENT_OFF(tmp_rinfo->t_garbage_collect);
+ list_delete_node(list, node);
+ rip_info_free(tmp_rinfo);
+ }
+
+ /* Update zebra. */
+ rip_zebra_ipv4_add(rip, rp);
+
+ /* Set the route change flag. */
+ SET_FLAG(rinfo->flags, RIP_RTF_CHANGED);
+
+ /* Signal the output process to trigger an update. */
+ rip_event(rip, RIP_TRIGGERED_UPDATE, 0);
+ }
+}
+
+/* Print out routes update time. */
+static void rip_vty_out_uptime(struct vty *vty, struct rip_info *rinfo)
+{
+ time_t clock;
+ struct tm tm;
+#define TIME_BUF 25
+ char timebuf[TIME_BUF];
+ struct event *thread;
+
+ if ((thread = rinfo->t_timeout) != NULL) {
+ clock = event_timer_remain_second(thread);
+ gmtime_r(&clock, &tm);
+ strftime(timebuf, TIME_BUF, "%M:%S", &tm);
+ vty_out(vty, "%5s", timebuf);
+ } else if ((thread = rinfo->t_garbage_collect) != NULL) {
+ clock = event_timer_remain_second(thread);
+ gmtime_r(&clock, &tm);
+ strftime(timebuf, TIME_BUF, "%M:%S", &tm);
+ vty_out(vty, "%5s", timebuf);
+ }
+}
+
+static const char *rip_route_type_print(int sub_type)
+{
+ switch (sub_type) {
+ case RIP_ROUTE_RTE:
+ return "n";
+ case RIP_ROUTE_STATIC:
+ return "s";
+ case RIP_ROUTE_DEFAULT:
+ return "d";
+ case RIP_ROUTE_REDISTRIBUTE:
+ return "r";
+ case RIP_ROUTE_INTERFACE:
+ return "i";
+ default:
+ return "?";
+ }
+}
+
+DEFUN (show_ip_rip,
+ show_ip_rip_cmd,
+ "show ip rip [vrf NAME]",
+ SHOW_STR
+ IP_STR
+ "Show RIP routes\n"
+ VRF_CMD_HELP_STR)
+{
+ struct rip *rip;
+ struct route_node *np;
+ struct rip_info *rinfo = NULL;
+ struct list *list = NULL;
+ struct listnode *listnode = NULL;
+ const char *vrf_name;
+ int idx = 0;
+
+ if (argv_find(argv, argc, "vrf", &idx))
+ vrf_name = argv[idx + 1]->arg;
+ else
+ vrf_name = VRF_DEFAULT_NAME;
+
+ rip = rip_lookup_by_vrf_name(vrf_name);
+ if (!rip) {
+ vty_out(vty, "%% RIP instance not found\n");
+ return CMD_SUCCESS;
+ }
+ if (!rip->enabled) {
+ vty_out(vty, "%% RIP instance is disabled\n");
+ return CMD_SUCCESS;
+ }
+
+ vty_out(vty,
+ "Codes: R - RIP, C - connected, S - Static, O - OSPF, B - BGP\n"
+ "Sub-codes:\n"
+ " (n) - normal, (s) - static, (d) - default, (r) - redistribute,\n"
+ " (i) - interface\n\n"
+ " Network Next Hop Metric From Tag Time\n");
+
+ for (np = route_top(rip->table); np; np = route_next(np)) {
+ list = np->info;
+
+ if (!list)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) {
+ int len;
+
+ len = vty_out(vty, "%c(%s) %pFX",
+ /* np->lock, For debugging. */
+ zebra_route_char(rinfo->type),
+ rip_route_type_print(rinfo->sub_type),
+ &np->p);
+
+ len = 24 - len;
+
+ if (len > 0)
+ vty_out(vty, "%*s", len, " ");
+
+ switch (rinfo->nh.type) {
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ vty_out(vty, "%-20pI4 %2d ",
+ &rinfo->nh.gate.ipv4, rinfo->metric);
+ break;
+ case NEXTHOP_TYPE_IFINDEX:
+ vty_out(vty, "0.0.0.0 %2d ",
+ rinfo->metric);
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ vty_out(vty, "blackhole %2d ",
+ rinfo->metric);
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ vty_out(vty, "V6 Address Hidden %2d ",
+ rinfo->metric);
+ break;
+ }
+
+ /* Route which exist in kernel routing table. */
+ if ((rinfo->type == ZEBRA_ROUTE_RIP) &&
+ (rinfo->sub_type == RIP_ROUTE_RTE)) {
+ vty_out(vty, "%-15pI4 ", &rinfo->from);
+ vty_out(vty, "%3" ROUTE_TAG_PRI " ",
+ (route_tag_t)rinfo->tag);
+ rip_vty_out_uptime(vty, rinfo);
+ } else if (rinfo->metric == RIP_METRIC_INFINITY) {
+ vty_out(vty, "self ");
+ vty_out(vty, "%3" ROUTE_TAG_PRI " ",
+ (route_tag_t)rinfo->tag);
+ rip_vty_out_uptime(vty, rinfo);
+ } else {
+ if (rinfo->external_metric) {
+ len = vty_out(
+ vty, "self (%s:%d)",
+ zebra_route_string(rinfo->type),
+ rinfo->external_metric);
+ len = 16 - len;
+ if (len > 0)
+ vty_out(vty, "%*s", len, " ");
+ } else
+ vty_out(vty, "self ");
+ vty_out(vty, "%3" ROUTE_TAG_PRI,
+ (route_tag_t)rinfo->tag);
+ }
+
+ vty_out(vty, "\n");
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+/* Vincent: formerly, it was show_ip_protocols_rip: "show ip protocols" */
+DEFUN (show_ip_rip_status,
+ show_ip_rip_status_cmd,
+ "show ip rip [vrf NAME] status",
+ SHOW_STR
+ IP_STR
+ "Show RIP routes\n"
+ VRF_CMD_HELP_STR
+ "IP routing protocol process parameters and statistics\n")
+{
+ struct rip *rip;
+ struct interface *ifp;
+ struct rip_interface *ri;
+ extern const struct message ri_version_msg[];
+ const char *send_version;
+ const char *receive_version;
+ const char *vrf_name;
+ int idx = 0;
+
+ if (argv_find(argv, argc, "vrf", &idx))
+ vrf_name = argv[idx + 1]->arg;
+ else
+ vrf_name = VRF_DEFAULT_NAME;
+
+ rip = rip_lookup_by_vrf_name(vrf_name);
+ if (!rip) {
+ vty_out(vty, "%% RIP instance not found\n");
+ return CMD_SUCCESS;
+ }
+ if (!rip->enabled) {
+ vty_out(vty, "%% RIP instance is disabled\n");
+ return CMD_SUCCESS;
+ }
+
+ vty_out(vty, "Routing Protocol is \"rip\"\n");
+ vty_out(vty, " Sending updates every %u seconds with +/-50%%,",
+ rip->update_time);
+ vty_out(vty, " next due in %lu seconds\n",
+ event_timer_remain_second(rip->t_update));
+ vty_out(vty, " Timeout after %u seconds,", rip->timeout_time);
+ vty_out(vty, " garbage collect after %u seconds\n", rip->garbage_time);
+
+ /* Filtering status show. */
+ config_show_distribute(vty, rip->distribute_ctx);
+
+ /* Default metric information. */
+ vty_out(vty, " Default redistribution metric is %u\n",
+ rip->default_metric);
+
+ /* Redistribute information. */
+ vty_out(vty, " Redistributing:");
+ rip_show_redistribute_config(vty, rip);
+ vty_out(vty, "\n");
+
+ vty_out(vty, " Default version control: send version %s,",
+ lookup_msg(ri_version_msg, rip->version_send, NULL));
+ if (rip->version_recv == RI_RIP_VERSION_1_AND_2)
+ vty_out(vty, " receive any version \n");
+ else
+ vty_out(vty, " receive version %s \n",
+ lookup_msg(ri_version_msg, rip->version_recv, NULL));
+
+ vty_out(vty, " Interface Send Recv Key-chain\n");
+
+ FOR_ALL_INTERFACES (rip->vrf, ifp) {
+ ri = ifp->info;
+
+ if (!ri->running)
+ continue;
+
+ if (ri->enable_network || ri->enable_interface) {
+ if (ri->ri_send == RI_RIP_UNSPEC)
+ send_version =
+ lookup_msg(ri_version_msg,
+ rip->version_send, NULL);
+ else
+ send_version = lookup_msg(ri_version_msg,
+ ri->ri_send, NULL);
+
+ if (ri->ri_receive == RI_RIP_UNSPEC)
+ receive_version =
+ lookup_msg(ri_version_msg,
+ rip->version_recv, NULL);
+ else
+ receive_version = lookup_msg(
+ ri_version_msg, ri->ri_receive, NULL);
+
+ vty_out(vty, " %-17s%-3s %-3s %s\n", ifp->name,
+ send_version, receive_version,
+ ri->key_chain ? ri->key_chain : "");
+ }
+ }
+
+ vty_out(vty, " Routing for Networks:\n");
+ rip_show_network_config(vty, rip);
+
+ int found_passive = 0;
+ FOR_ALL_INTERFACES (rip->vrf, ifp) {
+ ri = ifp->info;
+
+ if ((ri->enable_network || ri->enable_interface) &&
+ ri->passive) {
+ if (!found_passive) {
+ vty_out(vty, " Passive Interface(s):\n");
+ found_passive = 1;
+ }
+ vty_out(vty, " %s\n", ifp->name);
+ }
+ }
+
+ vty_out(vty, " Routing Information Sources:\n");
+ vty_out(vty,
+ " Gateway BadPackets BadRoutes Distance Last Update\n");
+ rip_peer_display(vty, rip);
+
+ rip_distance_show(vty, rip);
+
+ return CMD_SUCCESS;
+}
+
+/* RIP configuration write function. */
+static int config_write_rip(struct vty *vty)
+{
+ struct rip *rip;
+ int write = 0;
+
+ RB_FOREACH(rip, rip_instance_head, &rip_instances) {
+ char xpath[XPATH_MAXLEN];
+ struct lyd_node *dnode;
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-ripd:ripd/instance[vrf='%s']", rip->vrf_name);
+
+ dnode = yang_dnode_get(running_config->dnode, xpath);
+ assert(dnode);
+
+ nb_cli_show_dnode_cmds(vty, dnode, false);
+
+ /* Distribute configuration. */
+ config_write_distribute(vty, rip->distribute_ctx);
+
+ vty_out(vty, "exit\n");
+
+ write = 1;
+ }
+
+ return write;
+}
+
+static int config_write_rip(struct vty *vty);
+/* RIP node structure. */
+static struct cmd_node rip_node = {
+ .name = "rip",
+ .node = RIP_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-router)# ",
+ .config_write = config_write_rip,
+};
+
+/* Distribute-list update functions. */
+static void rip_distribute_update(struct distribute_ctx *ctx,
+ struct distribute *dist)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+ struct access_list *alist;
+ struct prefix_list *plist;
+
+ if (!ctx->vrf || !dist->ifname)
+ return;
+
+ ifp = if_lookup_by_name(dist->ifname, ctx->vrf->vrf_id);
+ if (ifp == NULL)
+ return;
+
+ ri = ifp->info;
+
+ if (dist->list[DISTRIBUTE_V4_IN]) {
+ alist = access_list_lookup(AFI_IP,
+ dist->list[DISTRIBUTE_V4_IN]);
+ if (alist)
+ ri->list[RIP_FILTER_IN] = alist;
+ else
+ ri->list[RIP_FILTER_IN] = NULL;
+ } else
+ ri->list[RIP_FILTER_IN] = NULL;
+
+ if (dist->list[DISTRIBUTE_V4_OUT]) {
+ alist = access_list_lookup(AFI_IP,
+ dist->list[DISTRIBUTE_V4_OUT]);
+ if (alist)
+ ri->list[RIP_FILTER_OUT] = alist;
+ else
+ ri->list[RIP_FILTER_OUT] = NULL;
+ } else
+ ri->list[RIP_FILTER_OUT] = NULL;
+
+ if (dist->prefix[DISTRIBUTE_V4_IN]) {
+ plist = prefix_list_lookup(AFI_IP,
+ dist->prefix[DISTRIBUTE_V4_IN]);
+ if (plist)
+ ri->prefix[RIP_FILTER_IN] = plist;
+ else
+ ri->prefix[RIP_FILTER_IN] = NULL;
+ } else
+ ri->prefix[RIP_FILTER_IN] = NULL;
+
+ if (dist->prefix[DISTRIBUTE_V4_OUT]) {
+ plist = prefix_list_lookup(AFI_IP,
+ dist->prefix[DISTRIBUTE_V4_OUT]);
+ if (plist)
+ ri->prefix[RIP_FILTER_OUT] = plist;
+ else
+ ri->prefix[RIP_FILTER_OUT] = NULL;
+ } else
+ ri->prefix[RIP_FILTER_OUT] = NULL;
+}
+
+void rip_distribute_update_interface(struct interface *ifp)
+{
+ struct rip_interface *ri = ifp->info;
+ struct rip *rip = ri->rip;
+ struct distribute *dist;
+
+ if (!rip)
+ return;
+ dist = distribute_lookup(rip->distribute_ctx, ifp->name);
+ if (dist)
+ rip_distribute_update(rip->distribute_ctx, dist);
+}
+
+/* Update all interface's distribute list. */
+/* ARGSUSED */
+static void rip_distribute_update_all(struct prefix_list *notused)
+{
+ struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct interface *ifp;
+
+ FOR_ALL_INTERFACES (vrf, ifp)
+ rip_distribute_update_interface(ifp);
+}
+/* ARGSUSED */
+static void rip_distribute_update_all_wrapper(struct access_list *notused)
+{
+ rip_distribute_update_all(NULL);
+}
+
+/* Delete all added rip route. */
+void rip_clean(struct rip *rip)
+{
+ rip_interfaces_clean(rip);
+
+ if (rip->enabled)
+ rip_instance_disable(rip);
+
+ stream_free(rip->obuf);
+
+ for (int i = 0; i < ZEBRA_ROUTE_MAX; i++)
+ if (rip->redist[i].route_map.name)
+ free(rip->redist[i].route_map.name);
+
+ route_table_finish(rip->table);
+ route_table_finish(rip->neighbor);
+ list_delete(&rip->peer_list);
+ distribute_list_delete(&rip->distribute_ctx);
+ if_rmap_ctx_delete(rip->if_rmap_ctx);
+
+ rip_clean_network(rip);
+ rip_passive_nondefault_clean(rip);
+ vector_free(rip->enable_interface);
+ route_table_finish(rip->enable_network);
+ vector_free(rip->passive_nondefault);
+ list_delete(&rip->offset_list_master);
+ route_table_finish(rip->distance_table);
+
+ RB_REMOVE(rip_instance_head, &rip_instances, rip);
+ XFREE(MTYPE_RIP_BFD_PROFILE, rip->default_bfd_profile);
+ XFREE(MTYPE_RIP_VRF_NAME, rip->vrf_name);
+ XFREE(MTYPE_RIP, rip);
+}
+
+static void rip_if_rmap_update(struct if_rmap_ctx *ctx,
+ struct if_rmap *if_rmap)
+{
+ struct interface *ifp = NULL;
+ struct rip_interface *ri;
+ struct route_map *rmap;
+ struct vrf *vrf = NULL;
+
+ if (ctx->name)
+ vrf = vrf_lookup_by_name(ctx->name);
+ if (vrf)
+ ifp = if_lookup_by_name(if_rmap->ifname, vrf->vrf_id);
+ if (ifp == NULL)
+ return;
+
+ ri = ifp->info;
+ if (if_rmap->routemap[IF_RMAP_IN]) {
+ rmap = route_map_lookup_by_name(if_rmap->routemap[IF_RMAP_IN]);
+ if (rmap)
+ ri->routemap[IF_RMAP_IN] = rmap;
+ else
+ ri->routemap[IF_RMAP_IN] = NULL;
+ } else
+ ri->routemap[RIP_FILTER_IN] = NULL;
+
+ if (if_rmap->routemap[IF_RMAP_OUT]) {
+ rmap = route_map_lookup_by_name(if_rmap->routemap[IF_RMAP_OUT]);
+ if (rmap)
+ ri->routemap[IF_RMAP_OUT] = rmap;
+ else
+ ri->routemap[IF_RMAP_OUT] = NULL;
+ } else
+ ri->routemap[RIP_FILTER_OUT] = NULL;
+}
+
+void rip_if_rmap_update_interface(struct interface *ifp)
+{
+ struct rip_interface *ri = ifp->info;
+ struct rip *rip = ri->rip;
+ struct if_rmap *if_rmap;
+ struct if_rmap_ctx *ctx;
+
+ if (!rip)
+ return;
+ ctx = rip->if_rmap_ctx;
+ if (!ctx)
+ return;
+ if_rmap = if_rmap_lookup(ctx, ifp->name);
+ if (if_rmap)
+ rip_if_rmap_update(ctx, if_rmap);
+}
+
+static void rip_routemap_update_redistribute(struct rip *rip)
+{
+ for (int i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+ if (rip->redist[i].route_map.name) {
+ rip->redist[i].route_map.map = route_map_lookup_by_name(
+ rip->redist[i].route_map.name);
+ route_map_counter_increment(
+ rip->redist[i].route_map.map);
+ }
+ }
+}
+
+/* ARGSUSED */
+static void rip_routemap_update(const char *notused)
+{
+ struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct rip *rip;
+ struct interface *ifp;
+
+ FOR_ALL_INTERFACES (vrf, ifp)
+ rip_if_rmap_update_interface(ifp);
+
+ rip = vrf->info;
+ if (rip)
+ rip_routemap_update_redistribute(rip);
+}
+
+/* Link RIP instance to VRF. */
+static void rip_vrf_link(struct rip *rip, struct vrf *vrf)
+{
+ struct interface *ifp;
+
+ rip->vrf = vrf;
+ rip->distribute_ctx->vrf = vrf;
+ vrf->info = rip;
+
+ FOR_ALL_INTERFACES (vrf, ifp)
+ rip_interface_sync(ifp);
+}
+
+/* Unlink RIP instance from VRF. */
+static void rip_vrf_unlink(struct rip *rip, struct vrf *vrf)
+{
+ struct interface *ifp;
+
+ rip->vrf = NULL;
+ rip->distribute_ctx->vrf = NULL;
+ vrf->info = NULL;
+
+ FOR_ALL_INTERFACES (vrf, ifp)
+ rip_interface_sync(ifp);
+}
+
+static void rip_instance_enable(struct rip *rip, struct vrf *vrf, int sock)
+{
+ rip->sock = sock;
+
+ rip_vrf_link(rip, vrf);
+ rip->enabled = true;
+
+ /* Resend all redistribute requests. */
+ rip_redistribute_enable(rip);
+
+ /* Create read and timer thread. */
+ rip_event(rip, RIP_READ, rip->sock);
+ rip_event(rip, RIP_UPDATE_EVENT, 1);
+
+ rip_zebra_vrf_register(vrf);
+}
+
+static void rip_instance_disable(struct rip *rip)
+{
+ struct vrf *vrf = rip->vrf;
+ struct route_node *rp;
+
+ /* Clear RIP routes */
+ for (rp = route_top(rip->table); rp; rp = route_next(rp)) {
+ struct rip_info *rinfo;
+ struct list *list;
+ struct listnode *listnode;
+
+ if ((list = rp->info) == NULL)
+ continue;
+
+ rinfo = listgetdata(listhead(list));
+ if (rip_route_rte(rinfo))
+ rip_zebra_ipv4_delete(rip, rp);
+
+ for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) {
+ EVENT_OFF(rinfo->t_timeout);
+ EVENT_OFF(rinfo->t_garbage_collect);
+ rip_info_free(rinfo);
+ }
+ list_delete(&list);
+ rp->info = NULL;
+ route_unlock_node(rp);
+ }
+
+ /* Flush all redistribute requests. */
+ rip_redistribute_disable(rip);
+
+ /* Cancel RIP related timers. */
+ EVENT_OFF(rip->t_update);
+ EVENT_OFF(rip->t_triggered_update);
+ EVENT_OFF(rip->t_triggered_interval);
+
+ /* Cancel read thread. */
+ EVENT_OFF(rip->t_read);
+
+ /* Close RIP socket. */
+ close(rip->sock);
+ rip->sock = -1;
+
+ /* Clear existing peers. */
+ list_delete_all_node(rip->peer_list);
+
+ rip_zebra_vrf_deregister(vrf);
+
+ rip_vrf_unlink(rip, vrf);
+ rip->enabled = false;
+}
+
+static int rip_vrf_new(struct vrf *vrf)
+{
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("%s: VRF created: %s(%u)", __func__, vrf->name,
+ vrf->vrf_id);
+
+ return 0;
+}
+
+static int rip_vrf_delete(struct vrf *vrf)
+{
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("%s: VRF deleted: %s(%u)", __func__, vrf->name,
+ vrf->vrf_id);
+
+ return 0;
+}
+
+static int rip_vrf_enable(struct vrf *vrf)
+{
+ struct rip *rip;
+ int socket;
+
+ rip = rip_lookup_by_vrf_name(vrf->name);
+ if (!rip || rip->enabled)
+ return 0;
+
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("%s: VRF %s(%u) enabled", __func__, vrf->name,
+ vrf->vrf_id);
+
+ /* Activate the VRF RIP instance. */
+ if (!rip->enabled) {
+ socket = rip_create_socket(vrf);
+ if (socket < 0)
+ return -1;
+
+ rip_instance_enable(rip, vrf, socket);
+ }
+
+ return 0;
+}
+
+static int rip_vrf_disable(struct vrf *vrf)
+{
+ struct rip *rip;
+
+ rip = rip_lookup_by_vrf_name(vrf->name);
+ if (!rip || !rip->enabled)
+ return 0;
+
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("%s: VRF %s(%u) disabled", __func__, vrf->name,
+ vrf->vrf_id);
+
+ /* Deactivate the VRF RIP instance. */
+ if (rip->enabled)
+ rip_instance_disable(rip);
+
+ return 0;
+}
+
+void rip_vrf_init(void)
+{
+ vrf_init(rip_vrf_new, rip_vrf_enable, rip_vrf_disable, rip_vrf_delete);
+
+ vrf_cmd_init(NULL);
+}
+
+void rip_vrf_terminate(void)
+{
+ vrf_terminate();
+}
+
+/* Allocate new rip structure and set default value. */
+void rip_init(void)
+{
+ /* Install top nodes. */
+ install_node(&rip_node);
+
+ /* Install rip commands. */
+ install_element(VIEW_NODE, &show_ip_rip_cmd);
+ install_element(VIEW_NODE, &show_ip_rip_status_cmd);
+
+ install_default(RIP_NODE);
+
+ /* Debug related init. */
+ rip_debug_init();
+
+ /* Access list install. */
+ access_list_init();
+ access_list_add_hook(rip_distribute_update_all_wrapper);
+ access_list_delete_hook(rip_distribute_update_all_wrapper);
+
+ /* Prefix list initialize.*/
+ prefix_list_init();
+ prefix_list_add_hook(rip_distribute_update_all);
+ prefix_list_delete_hook(rip_distribute_update_all);
+
+ /* Route-map */
+ rip_route_map_init();
+
+ route_map_add_hook(rip_routemap_update);
+ route_map_delete_hook(rip_routemap_update);
+
+ if_rmap_init(RIP_NODE);
+}
diff --git a/ripd/ripd.h b/ripd/ripd.h
new file mode 100644
index 0000000..ac4a51f
--- /dev/null
+++ b/ripd/ripd.h
@@ -0,0 +1,544 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RIP related values and structures.
+ * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
+ */
+
+#ifndef _ZEBRA_RIP_H
+#define _ZEBRA_RIP_H
+
+#include "hook.h"
+#include "nexthop.h"
+#include "distribute.h"
+#include "memory.h"
+#include "bfd.h"
+
+/* RIP version number. */
+#define RIPv1 1
+#define RIPv2 2
+/* N.B. stuff will break if
+ (RIPv1 != RI_RIP_VERSION_1) || (RIPv2 != RI_RIP_VERSION_2) */
+
+
+/* RIP command list. */
+#define RIP_REQUEST 1
+#define RIP_RESPONSE 2
+#define RIP_TRACEON 3 /* Obsolete */
+#define RIP_TRACEOFF 4 /* Obsolete */
+#define RIP_POLL 5
+#define RIP_POLL_ENTRY 6
+#define RIP_COMMAND_MAX 7
+
+/* RIP metric infinity value.*/
+#define RIP_METRIC_INFINITY 16
+
+/* Normal RIP packet min and max size. */
+#define RIP_PACKET_MINSIZ 4
+#define RIP_PACKET_MAXSIZ 512
+
+#define RIP_HEADER_SIZE 4
+#define RIP_RTE_SIZE 20
+
+/* Max count of routing table entry in one rip packet. */
+#define RIP_MAX_RTE ((RIP_PACKET_MAXSIZ - RIP_HEADER_SIZE) / RIP_RTE_SIZE)
+
+/* RIP version 2 multicast address. */
+#ifndef INADDR_RIP_GROUP
+#define INADDR_RIP_GROUP 0xe0000009 /* 224.0.0.9 */
+#endif
+
+/* RIP peer timeout value. */
+#define RIP_PEER_TIMER_DEFAULT 180
+
+/* RIP port number. */
+#define RIP_PORT_DEFAULT 520
+#define RIP_VTY_PORT 2602
+
+/* Default configuration file name. */
+#define RIPD_DEFAULT_CONFIG "ripd.conf"
+
+/* RIP route types. */
+#define RIP_ROUTE_RTE 0
+#define RIP_ROUTE_STATIC 1
+#define RIP_ROUTE_DEFAULT 2
+#define RIP_ROUTE_REDISTRIBUTE 3
+#define RIP_ROUTE_INTERFACE 4
+
+/* RIPv2 special RTE family types */
+#define RIP_FAMILY_AUTH 0xffff
+
+/* RIPv2 authentication types, for RIP_FAMILY_AUTH RTE's */
+#define RIP_NO_AUTH 0
+#define RIP_AUTH_DATA 1
+#define RIP_AUTH_SIMPLE_PASSWORD 2
+#define RIP_AUTH_MD5 3
+
+/* RIPv2 Simple authentication */
+#define RIP_AUTH_SIMPLE_SIZE 16
+
+/* RIPv2 MD5 authentication. */
+#define RIP_AUTH_MD5_SIZE 16
+#define RIP_AUTH_MD5_COMPAT_SIZE RIP_RTE_SIZE
+
+/* YANG paths */
+#define RIP_INSTANCE "/frr-ripd:ripd/instance"
+#define RIP_IFACE "/frr-interface:lib/interface/frr-ripd:rip"
+
+DECLARE_MGROUP(RIPD);
+
+/* RIP structure. */
+struct rip {
+ RB_ENTRY(rip) entry;
+
+ /* VRF this routing instance is associated with. */
+ char *vrf_name;
+
+ /* VRF backpointer (might be NULL if the VRF doesn't exist). */
+ struct vrf *vrf;
+
+ /* Status of the routing instance. */
+ bool enabled;
+
+ /* RIP socket. */
+ int sock;
+
+ /* Default version of rip instance. */
+ int version_send; /* version 1 or 2 (but not both) */
+ int version_recv; /* version 1 or 2 or both */
+
+ /* Output buffer of RIP. */
+ struct stream *obuf;
+
+ /* RIP routing information base. */
+ struct route_table *table;
+
+ /* RIP static neighbors. */
+ struct route_table *neighbor;
+
+ /* Linked list of RIP peers. */
+ struct list *peer_list;
+
+ /* RIP threads. */
+ struct event *t_read;
+
+ /* Update and garbage timer. */
+ struct event *t_update;
+
+ /* Triggered update hack. */
+ int trigger;
+ struct event *t_triggered_update;
+ struct event *t_triggered_interval;
+
+ /* RIP timer values. */
+ uint32_t update_time;
+ uint32_t timeout_time;
+ uint32_t garbage_time;
+
+ /* RIP default metric. */
+ uint8_t default_metric;
+
+ /* RIP default distance. */
+ uint8_t distance;
+ struct route_table *distance_table;
+
+ /* RIP ECMP flag */
+ uint8_t ecmp;
+
+ /* Are we in passive-interface default mode? */
+ bool passive_default;
+
+ /* RIP enabled interfaces. */
+ vector enable_interface;
+
+ /* RIP enabled networks. */
+ struct route_table *enable_network;
+
+ /* Vector to store passive-interface name. */
+ vector passive_nondefault;
+
+ /* RIP offset-lists. */
+ struct list *offset_list_master;
+
+ /* RIP redistribute configuration. */
+ struct {
+ bool enabled;
+ struct {
+ char *name;
+ struct route_map *map;
+ } route_map;
+ bool metric_config;
+ uint8_t metric;
+ } redist[ZEBRA_ROUTE_MAX];
+
+ /* For distribute-list container */
+ struct distribute_ctx *distribute_ctx;
+
+ /* For if_rmap container */
+ struct if_rmap_ctx *if_rmap_ctx;
+
+ /* Counters for SNMP. */
+ struct {
+ /* RIP route changes. */
+ long route_changes;
+
+ /* RIP queries. */
+ long queries;
+ } counters;
+
+ /* Default BFD profile to use with BFD sessions. */
+ char *default_bfd_profile;
+};
+RB_HEAD(rip_instance_head, rip);
+RB_PROTOTYPE(rip_instance_head, rip, entry, rip_instance_compare)
+
+/* RIP routing table entry which belong to rip_packet. */
+struct rte {
+ uint16_t family; /* Address family of this route. */
+ uint16_t tag; /* Route Tag which included in RIP2 packet. */
+ struct in_addr prefix; /* Prefix of rip route. */
+ struct in_addr mask; /* Netmask of rip route. */
+ struct in_addr nexthop; /* Next hop of rip route. */
+ uint32_t metric; /* Metric value of rip route. */
+};
+
+/* RIP packet structure. */
+struct rip_packet {
+ unsigned char command; /* Command type of RIP packet. */
+ unsigned char version; /* RIP version which coming from peer. */
+ unsigned char pad1; /* Padding of RIP packet header. */
+ unsigned char pad2; /* Same as above. */
+ struct rte rte[1]; /* Address structure. */
+};
+
+/* Buffer to read RIP packet. */
+union rip_buf {
+ struct rip_packet rip_packet;
+ char buf[RIP_PACKET_MAXSIZ];
+};
+
+/* RIP route information. */
+struct rip_info {
+ /* This route's type. */
+ int type;
+
+ /* Sub type. */
+ int sub_type;
+
+ /* RIP nexthop. */
+ struct nexthop nh;
+ struct in_addr from;
+
+ /* Metric of this route. */
+ uint32_t metric;
+
+ /* External metric of this route.
+ if learnt from an externalm proto */
+ uint32_t external_metric;
+
+ /* Tag information of this route. */
+ uint16_t tag;
+
+/* Flags of RIP route. */
+#define RIP_RTF_FIB 1
+#define RIP_RTF_CHANGED 2
+ uint8_t flags;
+
+ /* Garbage collect timer. */
+ struct event *t_timeout;
+ struct event *t_garbage_collect;
+
+ /* Route-map futures - this variables can be changed. */
+ struct in_addr nexthop_out;
+ uint8_t metric_set;
+ uint32_t metric_out;
+ uint16_t tag_out;
+ ifindex_t ifindex_out;
+
+ struct route_node *rp;
+
+ uint8_t distance;
+};
+
+typedef enum {
+ RIP_NO_SPLIT_HORIZON = 0,
+ RIP_SPLIT_HORIZON,
+ RIP_SPLIT_HORIZON_POISONED_REVERSE
+} split_horizon_policy_t;
+
+/* RIP specific interface configuration. */
+struct rip_interface {
+ /* Parent routing instance. */
+ struct rip *rip;
+
+ /* Interface data from zebra. */
+ struct interface *ifp;
+
+ /* RIP is enabled on this interface. */
+ int enable_network;
+ int enable_interface;
+
+ /* RIP is running on this interface. */
+ int running;
+
+ /* RIP version control. */
+ int ri_send;
+ int ri_receive;
+
+ /* RIPv2 broadcast mode */
+ bool v2_broadcast;
+
+ /* RIPv2 authentication type. */
+ int auth_type;
+
+ /* RIPv2 authentication string. */
+ char *auth_str;
+
+ /* RIPv2 authentication key chain. */
+ char *key_chain;
+
+ /* value to use for md5->auth_len */
+ int md5_auth_len;
+
+ /* Split horizon flag. */
+ split_horizon_policy_t split_horizon;
+
+/* For filter type slot. */
+#define RIP_FILTER_IN 0
+#define RIP_FILTER_OUT 1
+#define RIP_FILTER_MAX 2
+
+ /* Access-list. */
+ struct access_list *list[RIP_FILTER_MAX];
+
+ /* Prefix-list. */
+ struct prefix_list *prefix[RIP_FILTER_MAX];
+
+ /* Route-map. */
+ struct route_map *routemap[RIP_FILTER_MAX];
+
+ /* Wake up thread. */
+ struct event *t_wakeup;
+
+ /* Interface statistics. */
+ int recv_badpackets;
+ int recv_badroutes;
+ int sent_updates;
+
+ /* Passive interface. */
+ int passive;
+
+ /* BFD information. */
+ struct {
+ bool enabled;
+ char *profile;
+ } bfd;
+};
+
+/* RIP peer information. */
+struct rip_peer {
+ /* Parent routing instance. */
+ struct rip *rip;
+
+ /* Back-pointer to RIP interface. */
+ struct rip_interface *ri;
+
+ /* Peer address. */
+ struct in_addr addr;
+
+ /* Peer RIP tag value. */
+ int domain;
+
+ /* Last update time. */
+ time_t uptime;
+
+ /* Peer RIP version. */
+ uint8_t version;
+
+ /* Statistics. */
+ int recv_badpackets;
+ int recv_badroutes;
+
+ /* Timeout thread. */
+ struct event *t_timeout;
+
+ /* BFD information */
+ struct bfd_session_params *bfd_session;
+};
+
+struct rip_distance {
+ /* Distance value for the IP source prefix. */
+ uint8_t distance;
+
+ /* Name of the access-list to be matched. */
+ char *access_list;
+};
+
+struct rip_md5_info {
+ uint16_t family;
+ uint16_t type;
+ uint16_t packet_len;
+ uint8_t keyid;
+ uint8_t auth_len;
+ uint32_t sequence;
+ uint32_t reserv1;
+ uint32_t reserv2;
+};
+
+struct rip_md5_data {
+ uint16_t family;
+ uint16_t type;
+ uint8_t digest[16];
+};
+
+/* RIP accepet/announce methods. */
+#define RI_RIP_UNSPEC 0
+#define RI_RIP_VERSION_1 1
+#define RI_RIP_VERSION_2 2
+#define RI_RIP_VERSION_1_AND_2 3
+#define RI_RIP_VERSION_NONE 4
+/* N.B. stuff will break if
+ (RIPv1 != RI_RIP_VERSION_1) || (RIPv2 != RI_RIP_VERSION_2) */
+
+/* RIP event. */
+enum rip_event {
+ RIP_READ,
+ RIP_UPDATE_EVENT,
+ RIP_TRIGGERED_UPDATE,
+};
+
+/* Macro for timer turn on. */
+#define RIP_TIMER_ON(T, F, V) event_add_timer(master, (F), rinfo, (V), &(T))
+
+#define RIP_OFFSET_LIST_IN 0
+#define RIP_OFFSET_LIST_OUT 1
+#define RIP_OFFSET_LIST_MAX 2
+
+struct rip_offset_list {
+ /* Parent routing instance. */
+ struct rip *rip;
+
+ char *ifname;
+
+ struct {
+ char *alist_name;
+ /* struct access_list *alist; */
+ uint8_t metric;
+ } direct[RIP_OFFSET_LIST_MAX];
+};
+
+/* Prototypes. */
+extern void rip_init(void);
+extern void rip_clean(struct rip *rip);
+extern void rip_clean_network(struct rip *rip);
+extern void rip_interfaces_clean(struct rip *rip);
+extern int rip_passive_nondefault_set(struct rip *rip, const char *ifname);
+extern int rip_passive_nondefault_unset(struct rip *rip, const char *ifname);
+extern void rip_passive_nondefault_clean(struct rip *rip);
+extern void rip_if_init(void);
+extern void rip_route_map_init(void);
+extern void rip_zebra_vrf_register(struct vrf *vrf);
+extern void rip_zebra_vrf_deregister(struct vrf *vrf);
+extern void rip_zclient_init(struct event_loop *e);
+extern void rip_zclient_stop(void);
+extern int if_check_address(struct rip *rip, struct in_addr addr);
+extern struct rip *rip_lookup_by_vrf_id(vrf_id_t vrf_id);
+extern struct rip *rip_lookup_by_vrf_name(const char *vrf_name);
+extern struct rip *rip_create(const char *vrf_name, struct vrf *vrf,
+ int socket);
+
+extern int rip_request_send(struct sockaddr_in *, struct interface *, uint8_t,
+ struct connected *);
+extern int rip_neighbor_lookup(struct rip *rip, struct sockaddr_in *from);
+extern int rip_neighbor_add(struct rip *rip, struct prefix_ipv4 *p);
+extern int rip_neighbor_delete(struct rip *rip, struct prefix_ipv4 *p);
+
+extern int rip_enable_network_add(struct rip *rip, struct prefix *p);
+extern int rip_enable_network_delete(struct rip *rip, struct prefix *p);
+extern int rip_enable_if_add(struct rip *rip, const char *ifname);
+extern int rip_enable_if_delete(struct rip *rip, const char *ifname);
+
+extern void rip_event(struct rip *rip, enum rip_event event, int sock);
+extern void rip_ecmp_disable(struct rip *rip);
+
+extern int rip_create_socket(struct vrf *vrf);
+
+extern int rip_redistribute_check(struct rip *rip, int type);
+extern void rip_redistribute_conf_update(struct rip *rip, int type);
+extern void rip_redistribute_conf_delete(struct rip *rip, int type);
+extern void rip_redistribute_add(struct rip *rip, int type, int sub_type,
+ struct prefix_ipv4 *p, struct nexthop *nh,
+ unsigned int metric, unsigned char distance,
+ route_tag_t tag);
+extern void rip_redistribute_delete(struct rip *rip, int type, int sub_type,
+ struct prefix_ipv4 *p, ifindex_t ifindex);
+extern void rip_redistribute_withdraw(struct rip *rip, int type);
+extern void rip_zebra_ipv4_add(struct rip *rip, struct route_node *rp);
+extern void rip_zebra_ipv4_delete(struct rip *rip, struct route_node *rp);
+extern void rip_interface_multicast_set(int, struct connected *);
+extern void rip_distribute_update_interface(struct interface *);
+extern void rip_if_rmap_update_interface(struct interface *ifp);
+
+extern int rip_show_network_config(struct vty *vty, struct rip *rip);
+extern void rip_show_redistribute_config(struct vty *vty, struct rip *rip);
+
+extern void rip_peer_free(struct rip_peer *peer);
+extern void rip_peer_update(struct rip *rip, struct rip_interface *ri,
+ struct sockaddr_in *from, uint8_t version);
+extern void rip_peer_bad_route(struct rip *rip, struct rip_interface *ri,
+ struct sockaddr_in *from);
+extern void rip_peer_bad_packet(struct rip *rip, struct rip_interface *ri,
+ struct sockaddr_in *from);
+extern void rip_peer_display(struct vty *vty, struct rip *rip);
+extern struct rip_peer *rip_peer_lookup(struct rip *rip, struct in_addr *addr);
+extern struct rip_peer *rip_peer_lookup_next(struct rip *rip,
+ struct in_addr *addr);
+extern int rip_peer_list_cmp(struct rip_peer *p1, struct rip_peer *p2);
+extern void rip_peer_list_del(void *arg);
+void rip_peer_delete_routes(const struct rip_peer *peer);
+
+extern void rip_info_free(struct rip_info *);
+extern struct rip *rip_info_get_instance(const struct rip_info *rinfo);
+extern struct rip_distance *rip_distance_new(void);
+extern void rip_distance_free(struct rip_distance *rdistance);
+extern uint8_t rip_distance_apply(struct rip *rip, struct rip_info *rinfo);
+extern void rip_redistribute_enable(struct rip *rip);
+extern void rip_redistribute_disable(struct rip *rip);
+
+extern int rip_route_rte(struct rip_info *rinfo);
+extern struct rip_info *rip_ecmp_add(struct rip *rip,
+ struct rip_info *rinfo_new);
+extern struct rip_info *rip_ecmp_replace(struct rip *rip,
+ struct rip_info *rinfo_new);
+extern struct rip_info *rip_ecmp_delete(struct rip *rip,
+ struct rip_info *rinfo);
+
+extern struct rip_offset_list *rip_offset_list_new(struct rip *rip,
+ const char *ifname);
+extern void offset_list_del(struct rip_offset_list *offset);
+extern void offset_list_free(struct rip_offset_list *offset);
+extern struct rip_offset_list *rip_offset_list_lookup(struct rip *rip,
+ const char *ifname);
+extern int rip_offset_list_apply_in(struct prefix_ipv4 *, struct interface *,
+ uint32_t *);
+extern int rip_offset_list_apply_out(struct prefix_ipv4 *, struct interface *,
+ uint32_t *);
+extern int offset_list_cmp(struct rip_offset_list *o1,
+ struct rip_offset_list *o2);
+
+extern void rip_vrf_init(void);
+extern void rip_vrf_terminate(void);
+extern void rip_cli_init(void);
+
+extern struct zebra_privs_t ripd_privs;
+extern struct rip_instance_head rip_instances;
+
+/* Master thread structure. */
+extern struct event_loop *master;
+
+DECLARE_HOOK(rip_ifaddr_add, (struct connected * ifc), (ifc));
+DECLARE_HOOK(rip_ifaddr_del, (struct connected * ifc), (ifc));
+
+extern void rip_ecmp_change(struct rip *rip);
+
+extern uint32_t zebra_ecmp_count;
+
+#endif /* _ZEBRA_RIP_H */
diff --git a/ripd/subdir.am b/ripd/subdir.am
new file mode 100644
index 0000000..c793a6d
--- /dev/null
+++ b/ripd/subdir.am
@@ -0,0 +1,57 @@
+#
+# ripd
+#
+
+if RIPD
+sbin_PROGRAMS += ripd/ripd
+vtysh_daemons += ripd
+
+if SNMP
+module_LTLIBRARIES += ripd/ripd_snmp.la
+endif
+man8 += $(MANBUILD)/frr-ripd.8
+endif
+
+ripd_ripd_SOURCES = \
+ ripd/rip_bfd.c \
+ ripd/rip_cli.c \
+ ripd/rip_debug.c \
+ ripd/rip_errors.c \
+ ripd/rip_interface.c \
+ ripd/rip_offset.c \
+ ripd/rip_main.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 \
+ ripd/ripd.c \
+ # end
+
+clippy_scan += \
+ ripd/rip_bfd.c \
+ ripd/rip_cli.c \
+ # end
+
+noinst_HEADERS += \
+ ripd/rip_bfd.h \
+ ripd/rip_debug.h \
+ ripd/rip_errors.h \
+ ripd/rip_interface.h \
+ ripd/rip_nb.h \
+ ripd/ripd.h \
+ # end
+
+ripd_ripd_LDADD = lib/libfrr.la $(LIBCAP)
+nodist_ripd_ripd_SOURCES = \
+ yang/frr-ripd.yang.c \
+ yang/frr-bfdd.yang.c \
+ # end
+
+ripd_ripd_snmp_la_SOURCES = ripd/rip_snmp.c
+ripd_ripd_snmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
+ripd_ripd_snmp_la_LDFLAGS = $(MODULE_LDFLAGS)
+ripd_ripd_snmp_la_LIBADD = lib/libfrrsnmp.la