summaryrefslogtreecommitdiffstats
path: root/ripngd
diff options
context:
space:
mode:
Diffstat (limited to 'ripngd')
-rw-r--r--ripngd/.gitignore2
-rw-r--r--ripngd/Makefile10
-rw-r--r--ripngd/ripng_cli.c589
-rw-r--r--ripngd/ripng_debug.c227
-rw-r--r--ripngd/ripng_debug.h34
-rw-r--r--ripngd/ripng_interface.c882
-rw-r--r--ripngd/ripng_main.c171
-rw-r--r--ripngd/ripng_nb.c262
-rw-r--r--ripngd/ripng_nb.h137
-rw-r--r--ripngd/ripng_nb_config.c754
-rw-r--r--ripngd/ripng_nb_rpcs.c94
-rw-r--r--ripngd/ripng_nb_state.c220
-rw-r--r--ripngd/ripng_nexthop.c209
-rw-r--r--ripngd/ripng_nexthop.h49
-rw-r--r--ripngd/ripng_offset.c149
-rw-r--r--ripngd/ripng_peer.c164
-rw-r--r--ripngd/ripng_route.c152
-rw-r--r--ripngd/ripng_route.h40
-rw-r--r--ripngd/ripng_routemap.c423
-rw-r--r--ripngd/ripng_zebra.c250
-rw-r--r--ripngd/ripngd.c2718
-rw-r--r--ripngd/ripngd.h439
-rw-r--r--ripngd/subdir.am44
23 files changed, 8019 insertions, 0 deletions
diff --git a/ripngd/.gitignore b/ripngd/.gitignore
new file mode 100644
index 0000000..e6a8ee6
--- /dev/null
+++ b/ripngd/.gitignore
@@ -0,0 +1,2 @@
+ripngd
+ripngd.conf
diff --git a/ripngd/Makefile b/ripngd/Makefile
new file mode 100644
index 0000000..5b76bb2
--- /dev/null
+++ b/ripngd/Makefile
@@ -0,0 +1,10 @@
+all: ALWAYS
+ @$(MAKE) -s -C .. ripngd/ripngd
+%: ALWAYS
+ @$(MAKE) -s -C .. ripngd/$@
+
+Makefile:
+ #nothing
+ALWAYS:
+.PHONY: ALWAYS makefiles
+.SUFFIXES:
diff --git a/ripngd/ripng_cli.c b/ripngd/ripng_cli.c
new file mode 100644
index 0000000..9a96e29
--- /dev/null
+++ b/ripngd/ripng_cli.c
@@ -0,0 +1,589 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ * 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 "ripngd/ripngd.h"
+#include "ripngd/ripng_nb.h"
+#include "ripngd/ripng_cli_clippy.c"
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance
+ */
+DEFPY_YANG_NOSH (router_ripng,
+ router_ripng_cmd,
+ "router ripng [vrf NAME]",
+ "Enable a routing process\n"
+ "Make RIPng instance command\n"
+ VRF_CMD_HELP_STR)
+{
+ char xpath[XPATH_MAXLEN];
+ int ret;
+
+ /* Build RIPng instance XPath. */
+ if (!vrf)
+ vrf = VRF_DEFAULT_NAME;
+ snprintf(xpath, sizeof(xpath), "/frr-ripngd:ripngd/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(RIPNG_NODE, xpath);
+
+ return ret;
+}
+
+DEFPY_YANG (no_router_ripng,
+ no_router_ripng_cmd,
+ "no router ripng [vrf NAME]",
+ NO_STR
+ "Enable a routing process\n"
+ "Make RIPng instance command\n"
+ VRF_CMD_HELP_STR)
+{
+ char xpath[XPATH_MAXLEN];
+
+ /* Build RIPng instance XPath. */
+ if (!vrf)
+ vrf = VRF_DEFAULT_NAME;
+ snprintf(xpath, sizeof(xpath), "/frr-ripngd:ripngd/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_ripng(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 ripng");
+ if (!strmatch(vrf_name, VRF_DEFAULT_NAME))
+ vty_out(vty, " vrf %s", vrf_name);
+ vty_out(vty, "\n");
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/allow-ecmp
+ */
+DEFUN_YANG (ripng_allow_ecmp,
+ ripng_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_ripng_allow_ecmp,
+ no_ripng_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_ripng_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-ripngd:ripngd/instance/default-information-originate
+ */
+DEFPY_YANG (ripng_default_information_originate,
+ ripng_default_information_originate_cmd,
+ "[no] default-information originate",
+ NO_STR
+ "Default route information\n"
+ "Distribute 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_ripng_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-ripngd:ripngd/instance/default-metric
+ */
+DEFPY_YANG (ripng_default_metric,
+ ripng_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_ripng_default_metric,
+ no_ripng_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_ripng_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-ripngd:ripngd/instance/network
+ */
+DEFPY_YANG (ripng_network_prefix,
+ ripng_network_prefix_cmd,
+ "[no] network X:X::X:X/M",
+ NO_STR
+ "RIPng enable on specified interface or network.\n"
+ "IPv6 network\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_ripng_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-ripngd:ripngd/instance/interface
+ */
+DEFPY_YANG (ripng_network_if,
+ ripng_network_if_cmd,
+ "[no] network WORD",
+ NO_STR
+ "RIPng enable on specified interface or 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_ripng_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-ripngd:ripngd/instance/offset-list
+ */
+DEFPY_YANG (ripng_offset_list,
+ ripng_offset_list_cmd,
+ "[no] offset-list ACCESSLIST6_NAME$acl <in|out>$direction (0-16)$metric [IFNAME]",
+ NO_STR
+ "Modify RIPng 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_ripng_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-ripngd:ripngd/instance/passive-interface
+ */
+DEFPY_YANG (ripng_passive_interface,
+ ripng_passive_interface_cmd,
+ "[no] passive-interface IFNAME",
+ NO_STR
+ "Suppress routing updates on an interface\n"
+ "Interface name\n")
+{
+ 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_ripng_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));
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/redistribute
+ */
+DEFPY_YANG (ripng_redistribute,
+ ripng_redistribute_cmd,
+ "[no] redistribute " FRR_REDIST_STR_RIPNGD "$protocol [{metric (0-16)|route-map RMAP_NAME$route_map}]",
+ NO_STR
+ REDIST_STR
+ FRR_REDIST_HELP_STR_RIPNGD
+ "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_ripng_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-ripngd:ripngd/instance/static-route
+ */
+DEFPY_YANG (ripng_route,
+ ripng_route_cmd,
+ "[no] route X:X::X:X/M",
+ NO_STR
+ "Static route setup\n"
+ "Set static RIPng route announcement\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_ripng_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-ripngd:ripngd/instance/aggregate-addres
+ */
+DEFPY_YANG (ripng_aggregate_address,
+ ripng_aggregate_address_cmd,
+ "[no] aggregate-address X:X::X:X/M",
+ NO_STR
+ "Set aggregate RIPng route announcement\n"
+ "Aggregate network\n")
+{
+ nb_cli_enqueue_change(vty, "./aggregate-address",
+ no ? NB_OP_DESTROY : NB_OP_CREATE,
+ aggregate_address_str);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_ripng_aggregate_address(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " aggregate-address %s\n",
+ yang_dnode_get_string(dnode, NULL));
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/timers
+ */
+DEFPY_YANG (ripng_timers,
+ ripng_timers_cmd,
+ "timers basic (1-65535)$update (1-65535)$timeout (1-65535)$garbage",
+ "RIPng timers setup\n"
+ "Basic timer\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_ripng_timers,
+ no_ripng_timers_cmd,
+ "no timers basic [(1-65535) (1-65535) (1-65535)]",
+ NO_STR
+ "RIPng timers setup\n"
+ "Basic timer\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_ripng_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-interface:lib/interface/frr-ripngd:ripng/split-horizon
+ */
+DEFPY_YANG (ipv6_ripng_split_horizon,
+ ipv6_ripng_split_horizon_cmd,
+ "[no] ipv6 ripng split-horizon [poisoned-reverse$poisoned_reverse]",
+ NO_STR
+ IPV6_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-ripngd:ripng");
+}
+
+void cli_show_ipv6_ripng_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 RIPNG_NO_SPLIT_HORIZON:
+ vty_out(vty, " no ipv6 ripng split-horizon\n");
+ break;
+ case RIPNG_SPLIT_HORIZON:
+ vty_out(vty, " ipv6 ripng split-horizon\n");
+ break;
+ case RIPNG_SPLIT_HORIZON_POISONED_REVERSE:
+ vty_out(vty, " ipv6 ripng split-horizon poisoned-reverse\n");
+ break;
+ }
+}
+
+/*
+ * XPath: /frr-ripngd:clear-ripng-route
+ */
+DEFPY_YANG (clear_ipv6_rip,
+ clear_ipv6_rip_cmd,
+ "clear ipv6 ripng [vrf WORD]",
+ CLEAR_STR
+ IPV6_STR
+ "Clear IPv6 RIP database\n"
+ VRF_CMD_HELP_STR)
+{
+ struct list *input;
+ int ret;
+
+ input = list_new();
+ if (vrf) {
+ struct yang_data *yang_vrf;
+
+ yang_vrf = yang_data_new(
+ "/frr-ripngd:clear-ripng-route/input/vrf", vrf);
+ listnode_add(input, yang_vrf);
+ }
+
+ ret = nb_cli_rpc(vty, "/frr-ripngd:clear-ripng-route", input, NULL);
+
+ list_delete(&input);
+
+ return ret;
+}
+
+DEFUN (ripng_ipv6_distribute_list,
+ ripng_ipv6_distribute_list_cmd,
+ "ipv6 distribute-list [prefix] ACCESSLIST6_NAME <in|out> [WORD]",
+ "IPv6\n"
+ "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_parser(prefix, false, argv[3 + prefix]->text,
+ argv[2 + prefix]->arg, ifname);
+}
+
+DEFUN (ripng_no_ipv6_distribute_list,
+ ripng_no_ipv6_distribute_list_cmd,
+ "no ipv6 distribute-list [prefix] ACCESSLIST6_NAME <in|out> [WORD]",
+ NO_STR
+ "IPv6\n"
+ "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[3]->type == WORD_TKN) ? 1 : 0;
+
+ if (argv[argc - 1]->type == VARIABLE_TKN)
+ ifname = argv[argc - 1]->arg;
+
+ return distribute_list_no_parser(vty, prefix, false,
+ argv[4 + prefix]->text,
+ argv[3 + prefix]->arg, ifname);
+}
+
+void ripng_cli_init(void)
+{
+ install_element(CONFIG_NODE, &router_ripng_cmd);
+ install_element(CONFIG_NODE, &no_router_ripng_cmd);
+
+ install_element(RIPNG_NODE, &ripng_ipv6_distribute_list_cmd);
+ install_element(RIPNG_NODE, &ripng_no_ipv6_distribute_list_cmd);
+
+ install_element(RIPNG_NODE, &ripng_allow_ecmp_cmd);
+ install_element(RIPNG_NODE, &no_ripng_allow_ecmp_cmd);
+ install_element(RIPNG_NODE, &ripng_default_information_originate_cmd);
+ install_element(RIPNG_NODE, &ripng_default_metric_cmd);
+ install_element(RIPNG_NODE, &no_ripng_default_metric_cmd);
+ install_element(RIPNG_NODE, &ripng_network_prefix_cmd);
+ install_element(RIPNG_NODE, &ripng_network_if_cmd);
+ install_element(RIPNG_NODE, &ripng_offset_list_cmd);
+ install_element(RIPNG_NODE, &ripng_passive_interface_cmd);
+ install_element(RIPNG_NODE, &ripng_redistribute_cmd);
+ install_element(RIPNG_NODE, &ripng_route_cmd);
+ install_element(RIPNG_NODE, &ripng_aggregate_address_cmd);
+ install_element(RIPNG_NODE, &ripng_timers_cmd);
+ install_element(RIPNG_NODE, &no_ripng_timers_cmd);
+
+ install_element(INTERFACE_NODE, &ipv6_ripng_split_horizon_cmd);
+
+ install_element(ENABLE_NODE, &clear_ipv6_rip_cmd);
+}
diff --git a/ripngd/ripng_debug.c b/ripngd/ripng_debug.c
new file mode 100644
index 0000000..5ddd7ac
--- /dev/null
+++ b/ripngd/ripng_debug.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RIPng debug output routines
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ */
+
+#include <zebra.h>
+#include "command.h"
+#include "ripngd/ripng_debug.h"
+
+/* For debug statement. */
+unsigned long ripng_debug_event = 0;
+unsigned long ripng_debug_packet = 0;
+unsigned long ripng_debug_zebra = 0;
+
+DEFUN_NOSH (show_debugging_ripng,
+ show_debugging_ripng_cmd,
+ "show debugging [ripng]",
+ SHOW_STR
+ DEBUG_STR
+ "RIPng configuration\n")
+{
+ vty_out(vty, "RIPng debugging status:\n");
+
+ if (IS_RIPNG_DEBUG_EVENT)
+ vty_out(vty, " RIPng event debugging is on\n");
+
+ if (IS_RIPNG_DEBUG_PACKET) {
+ if (IS_RIPNG_DEBUG_SEND && IS_RIPNG_DEBUG_RECV) {
+ vty_out(vty, " RIPng packet debugging is on\n");
+ } else {
+ if (IS_RIPNG_DEBUG_SEND)
+ vty_out(vty,
+ " RIPng packet send debugging is on\n");
+ else
+ vty_out(vty,
+ " RIPng packet receive debugging is on\n");
+ }
+ }
+
+ if (IS_RIPNG_DEBUG_ZEBRA)
+ vty_out(vty, " RIPng zebra debugging is on\n");
+
+ cmd_show_lib_debugs(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_ripng_events,
+ debug_ripng_events_cmd,
+ "debug ripng events",
+ DEBUG_STR
+ "RIPng configuration\n"
+ "Debug option set for ripng events\n")
+{
+ ripng_debug_event = RIPNG_DEBUG_EVENT;
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_ripng_packet,
+ debug_ripng_packet_cmd,
+ "debug ripng packet",
+ DEBUG_STR
+ "RIPng configuration\n"
+ "Debug option set for ripng packet\n")
+{
+ ripng_debug_packet = RIPNG_DEBUG_PACKET;
+ ripng_debug_packet |= RIPNG_DEBUG_SEND;
+ ripng_debug_packet |= RIPNG_DEBUG_RECV;
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_ripng_packet_direct,
+ debug_ripng_packet_direct_cmd,
+ "debug ripng packet <recv|send>",
+ DEBUG_STR
+ "RIPng configuration\n"
+ "Debug option set for ripng packet\n"
+ "Debug option set for receive packet\n"
+ "Debug option set for send packet\n")
+{
+ int idx_recv_send = 3;
+ ripng_debug_packet |= RIPNG_DEBUG_PACKET;
+ if (strcmp("send", argv[idx_recv_send]->text) == 0)
+ ripng_debug_packet |= RIPNG_DEBUG_SEND;
+ if (strcmp("recv", argv[idx_recv_send]->text) == 0)
+ ripng_debug_packet |= RIPNG_DEBUG_RECV;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_ripng_zebra,
+ debug_ripng_zebra_cmd,
+ "debug ripng zebra",
+ DEBUG_STR
+ "RIPng configuration\n"
+ "Debug option set for ripng and zebra communication\n")
+{
+ ripng_debug_zebra = RIPNG_DEBUG_ZEBRA;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ripng_events,
+ no_debug_ripng_events_cmd,
+ "no debug ripng events",
+ NO_STR
+ DEBUG_STR
+ "RIPng configuration\n"
+ "Debug option set for ripng events\n")
+{
+ ripng_debug_event = 0;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ripng_packet,
+ no_debug_ripng_packet_cmd,
+ "no debug ripng packet",
+ NO_STR
+ DEBUG_STR
+ "RIPng configuration\n"
+ "Debug option set for ripng packet\n")
+{
+ ripng_debug_packet = 0;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ripng_packet_direct,
+ no_debug_ripng_packet_direct_cmd,
+ "no debug ripng packet <recv|send>",
+ NO_STR
+ DEBUG_STR
+ "RIPng configuration\n"
+ "Debug option set for ripng packet\n"
+ "Debug option set for receive packet\n"
+ "Debug option set for send packet\n")
+{
+ int idx_recv_send = 4;
+ if (strcmp("send", argv[idx_recv_send]->text) == 0) {
+ if (IS_RIPNG_DEBUG_RECV)
+ ripng_debug_packet &= ~RIPNG_DEBUG_SEND;
+ else
+ ripng_debug_packet = 0;
+ } else if (strcmp("recv", argv[idx_recv_send]->text) == 0) {
+ if (IS_RIPNG_DEBUG_SEND)
+ ripng_debug_packet &= ~RIPNG_DEBUG_RECV;
+ else
+ ripng_debug_packet = 0;
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ripng_zebra,
+ no_debug_ripng_zebra_cmd,
+ "no debug ripng zebra",
+ NO_STR
+ DEBUG_STR
+ "RIPng configuration\n"
+ "Debug option set for ripng and zebra communication\n")
+{
+ ripng_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_RIPNG_DEBUG_EVENT) {
+ vty_out(vty, "debug ripng events\n");
+ write++;
+ }
+ if (IS_RIPNG_DEBUG_PACKET) {
+ if (IS_RIPNG_DEBUG_SEND && IS_RIPNG_DEBUG_RECV) {
+ vty_out(vty, "debug ripng packet\n");
+ write++;
+ } else {
+ if (IS_RIPNG_DEBUG_SEND)
+ vty_out(vty, "debug ripng packet send\n");
+ else
+ vty_out(vty, "debug ripng packet recv\n");
+ write++;
+ }
+ }
+ if (IS_RIPNG_DEBUG_ZEBRA) {
+ vty_out(vty, "debug ripng zebra\n");
+ write++;
+ }
+ return write;
+}
+
+void ripng_debug_init(void)
+{
+ ripng_debug_event = 0;
+ ripng_debug_packet = 0;
+ ripng_debug_zebra = 0;
+
+ install_node(&debug_node);
+
+ install_element(ENABLE_NODE, &show_debugging_ripng_cmd);
+
+ install_element(ENABLE_NODE, &debug_ripng_events_cmd);
+ install_element(ENABLE_NODE, &debug_ripng_packet_cmd);
+ install_element(ENABLE_NODE, &debug_ripng_packet_direct_cmd);
+ install_element(ENABLE_NODE, &debug_ripng_zebra_cmd);
+ install_element(ENABLE_NODE, &no_debug_ripng_events_cmd);
+ install_element(ENABLE_NODE, &no_debug_ripng_packet_cmd);
+ install_element(ENABLE_NODE, &no_debug_ripng_packet_direct_cmd);
+ install_element(ENABLE_NODE, &no_debug_ripng_zebra_cmd);
+
+ install_element(CONFIG_NODE, &debug_ripng_events_cmd);
+ install_element(CONFIG_NODE, &debug_ripng_packet_cmd);
+ install_element(CONFIG_NODE, &debug_ripng_packet_direct_cmd);
+ install_element(CONFIG_NODE, &debug_ripng_zebra_cmd);
+ install_element(CONFIG_NODE, &no_debug_ripng_events_cmd);
+ install_element(CONFIG_NODE, &no_debug_ripng_packet_cmd);
+ install_element(CONFIG_NODE, &no_debug_ripng_packet_direct_cmd);
+ install_element(CONFIG_NODE, &no_debug_ripng_zebra_cmd);
+}
diff --git a/ripngd/ripng_debug.h b/ripngd/ripng_debug.h
new file mode 100644
index 0000000..7af9206
--- /dev/null
+++ b/ripngd/ripng_debug.h
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RIPng debug output routines
+ * Copyright (C) 1998, 1999 Kunihiro Ishiguro
+ */
+
+#ifndef _ZEBRA_RIPNG_DEBUG_H
+#define _ZEBRA_RIPNG_DEBUG_H
+
+/* Debug flags. */
+#define RIPNG_DEBUG_EVENT 0x01
+
+#define RIPNG_DEBUG_PACKET 0x01
+#define RIPNG_DEBUG_SEND 0x20
+#define RIPNG_DEBUG_RECV 0x40
+
+#define RIPNG_DEBUG_ZEBRA 0x01
+
+/* Debug related macro. */
+#define IS_RIPNG_DEBUG_EVENT (ripng_debug_event & RIPNG_DEBUG_EVENT)
+
+#define IS_RIPNG_DEBUG_PACKET (ripng_debug_packet & RIPNG_DEBUG_PACKET)
+#define IS_RIPNG_DEBUG_SEND (ripng_debug_packet & RIPNG_DEBUG_SEND)
+#define IS_RIPNG_DEBUG_RECV (ripng_debug_packet & RIPNG_DEBUG_RECV)
+
+#define IS_RIPNG_DEBUG_ZEBRA (ripng_debug_zebra & RIPNG_DEBUG_ZEBRA)
+
+extern unsigned long ripng_debug_event;
+extern unsigned long ripng_debug_packet;
+extern unsigned long ripng_debug_zebra;
+
+extern void ripng_debug_init(void);
+
+#endif /* _ZEBRA_RIPNG_DEBUG_H */
diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c
new file mode 100644
index 0000000..4cb4bb5
--- /dev/null
+++ b/ripngd/ripng_interface.c
@@ -0,0 +1,882 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Interface related function for RIPng.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "if.h"
+#include "prefix.h"
+#include "memory.h"
+#include "network.h"
+#include "filter.h"
+#include "log.h"
+#include "stream.h"
+#include "zclient.h"
+#include "command.h"
+#include "agg_table.h"
+#include "frrevent.h"
+#include "privs.h"
+#include "vrf.h"
+#include "lib_errors.h"
+#include "northbound_cli.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_debug.h"
+
+/* If RFC2133 definition is used. */
+#ifndef IPV6_JOIN_GROUP
+#define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
+#endif
+#ifndef IPV6_LEAVE_GROUP
+#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP
+#endif
+
+DEFINE_MTYPE_STATIC(RIPNGD, RIPNG_IF, "ripng interface");
+
+/* Static utility function. */
+static void ripng_enable_apply(struct interface *);
+static void ripng_passive_interface_apply(struct interface *);
+static int ripng_enable_if_lookup(struct ripng *ripng, const char *ifname);
+static int ripng_enable_network_lookup2(struct connected *);
+static void ripng_enable_apply_all(struct ripng *ripng);
+
+/* Join to the all rip routers multicast group. */
+static int ripng_multicast_join(struct interface *ifp, int sock)
+{
+ int ret;
+ struct ipv6_mreq mreq;
+ int save_errno;
+
+ if (if_is_multicast(ifp)) {
+ memset(&mreq, 0, sizeof(mreq));
+ inet_pton(AF_INET6, RIPNG_GROUP, &mreq.ipv6mr_multiaddr);
+ mreq.ipv6mr_interface = ifp->ifindex;
+
+ /*
+ * NetBSD 1.6.2 requires root to join groups on gif(4).
+ * While this is bogus, privs are available and easy to use
+ * for this call as a workaround.
+ */
+ frr_with_privs(&ripngd_privs) {
+
+ ret = setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ (char *)&mreq, sizeof(mreq));
+ save_errno = errno;
+
+ }
+
+ if (ret < 0 && save_errno == EADDRINUSE) {
+ /*
+ * Group is already joined. This occurs due to sloppy
+ * group
+ * management, in particular declining to leave the
+ * group on
+ * an interface that has just gone down.
+ */
+ zlog_warn("ripng join on %s EADDRINUSE (ignoring)",
+ ifp->name);
+ return 0; /* not an error */
+ }
+
+ if (ret < 0)
+ zlog_warn("can't setsockopt IPV6_JOIN_GROUP: %s",
+ safe_strerror(save_errno));
+
+ if (IS_RIPNG_DEBUG_EVENT)
+ zlog_debug(
+ "RIPng %s join to all-rip-routers multicast group",
+ ifp->name);
+
+ if (ret < 0)
+ return -1;
+ }
+ return 0;
+}
+
+/* Leave from the all rip routers multicast group. */
+static int ripng_multicast_leave(struct interface *ifp, int sock)
+{
+ int ret;
+ struct ipv6_mreq mreq;
+
+ if (if_is_multicast(ifp)) {
+ memset(&mreq, 0, sizeof(mreq));
+ inet_pton(AF_INET6, RIPNG_GROUP, &mreq.ipv6mr_multiaddr);
+ mreq.ipv6mr_interface = ifp->ifindex;
+
+ ret = setsockopt(sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
+ (char *)&mreq, sizeof(mreq));
+ if (ret < 0)
+ zlog_warn("can't setsockopt IPV6_LEAVE_GROUP: %s",
+ safe_strerror(errno));
+
+ if (IS_RIPNG_DEBUG_EVENT)
+ zlog_debug(
+ "RIPng %s leave from all-rip-routers multicast group",
+ ifp->name);
+
+ if (ret < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* How many link local IPv6 address could be used on the interface ? */
+static int ripng_if_ipv6_lladdress_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_INET6) &&
+ IN6_IS_ADDR_LINKLOCAL(&p->u.prefix6))
+ count++;
+ }
+
+ return count;
+}
+
+static int ripng_if_down(struct interface *ifp)
+{
+ struct agg_node *rp;
+ struct ripng_info *rinfo;
+ struct ripng_interface *ri;
+ struct ripng *ripng;
+ struct list *list = NULL;
+ struct listnode *listnode = NULL, *nextnode = NULL;
+
+ ri = ifp->info;
+
+ EVENT_OFF(ri->t_wakeup);
+
+ ripng = ri->ripng;
+
+ if (ripng)
+ for (rp = agg_route_top(ripng->table); rp;
+ rp = agg_route_next(rp))
+ if ((list = rp->info) != NULL)
+ for (ALL_LIST_ELEMENTS(list, listnode, nextnode,
+ rinfo))
+ if (rinfo->ifindex == ifp->ifindex)
+ ripng_ecmp_delete(ripng, rinfo);
+
+
+ if (ri->running) {
+ if (IS_RIPNG_DEBUG_EVENT)
+ zlog_debug("turn off %s", ifp->name);
+
+ /* Leave from multicast group. */
+ if (ripng)
+ ripng_multicast_leave(ifp, ripng->sock);
+
+ ri->running = 0;
+ }
+
+ return 0;
+}
+
+/* Interface link up message processing. */
+static int ripng_ifp_up(struct interface *ifp)
+{
+ if (IS_RIPNG_DEBUG_ZEBRA)
+ zlog_debug(
+ "interface up %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->mtu6);
+
+ ripng_interface_sync(ifp);
+
+ /* Check if this interface is RIPng enabled or not. */
+ ripng_enable_apply(ifp);
+
+ /* Check for a passive interface. */
+ ripng_passive_interface_apply(ifp);
+
+ /* Apply distribute list to the all interface. */
+ ripng_distribute_update_interface(ifp);
+
+ return 0;
+}
+
+/* Interface link down message processing. */
+static int ripng_ifp_down(struct interface *ifp)
+{
+ ripng_interface_sync(ifp);
+ ripng_if_down(ifp);
+
+ if (IS_RIPNG_DEBUG_ZEBRA)
+ zlog_debug(
+ "interface down %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->mtu6);
+
+ return 0;
+}
+
+/* Interface addition message from zebra. */
+static int ripng_ifp_create(struct interface *ifp)
+{
+ ripng_interface_sync(ifp);
+
+ if (IS_RIPNG_DEBUG_ZEBRA)
+ zlog_debug(
+ "RIPng 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->mtu6);
+
+ /* Check is this interface is RIP enabled or not.*/
+ ripng_enable_apply(ifp);
+
+ /* Apply distribute list to the interface. */
+ ripng_distribute_update_interface(ifp);
+
+ /* Check interface routemap. */
+ ripng_if_rmap_update_interface(ifp);
+
+ return 0;
+}
+
+static int ripng_ifp_destroy(struct interface *ifp)
+{
+ ripng_interface_sync(ifp);
+ if (if_is_up(ifp)) {
+ ripng_if_down(ifp);
+ }
+
+ if (IS_RIPNG_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->mtu6);
+
+ return 0;
+}
+
+void ripng_interface_clean(struct ripng *ripng)
+{
+ struct interface *ifp;
+ struct ripng_interface *ri;
+
+ FOR_ALL_INTERFACES (ripng->vrf, ifp) {
+ ri = ifp->info;
+
+ ri->enable_network = 0;
+ ri->enable_interface = 0;
+ ri->running = 0;
+
+ EVENT_OFF(ri->t_wakeup);
+ }
+}
+
+static void ripng_apply_address_add(struct connected *ifc)
+{
+ struct ripng_interface *ri = ifc->ifp->info;
+ struct ripng *ripng = ri->ripng;
+ struct prefix_ipv6 address;
+ struct prefix *p;
+
+ if (!ripng)
+ return;
+
+ if (!if_is_up(ifc->ifp))
+ return;
+
+ p = ifc->address;
+
+ memset(&address, 0, sizeof(address));
+ address.family = p->family;
+ address.prefix = p->u.prefix6;
+ address.prefixlen = p->prefixlen;
+ apply_mask_ipv6(&address);
+
+ /* Check if this interface is RIP enabled or not
+ or Check if this address's prefix is RIP enabled */
+ if ((ripng_enable_if_lookup(ripng, ifc->ifp->name) >= 0)
+ || (ripng_enable_network_lookup2(ifc) >= 0))
+ ripng_redistribute_add(ripng, ZEBRA_ROUTE_CONNECT,
+ RIPNG_ROUTE_INTERFACE, &address,
+ ifc->ifp->ifindex, NULL, 0);
+}
+
+int ripng_interface_address_add(ZAPI_CALLBACK_ARGS)
+{
+ struct connected *c;
+ struct prefix *p;
+
+ c = zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD,
+ zclient->ibuf, vrf_id);
+
+ if (c == NULL)
+ return 0;
+
+ p = c->address;
+
+ if (p->family == AF_INET6) {
+ struct ripng_interface *ri = c->ifp->info;
+
+ if (IS_RIPNG_DEBUG_ZEBRA)
+ zlog_debug("RIPng connected address %pFX add", p);
+
+ /* Check is this prefix needs to be redistributed. */
+ ripng_apply_address_add(c);
+
+ /* Let's try once again whether the interface could be activated
+ */
+ if (!ri->running) {
+ /* Check if this interface is RIP enabled or not.*/
+ ripng_enable_apply(c->ifp);
+
+ /* Apply distribute list to the interface. */
+ ripng_distribute_update_interface(c->ifp);
+
+ /* Check interface routemap. */
+ ripng_if_rmap_update_interface(c->ifp);
+ }
+ }
+
+ return 0;
+}
+
+static void ripng_apply_address_del(struct connected *ifc)
+{
+ struct ripng_interface *ri = ifc->ifp->info;
+ struct ripng *ripng = ri->ripng;
+ struct prefix_ipv6 address;
+ struct prefix *p;
+
+ if (!ripng)
+ return;
+
+ if (!if_is_up(ifc->ifp))
+ return;
+
+ p = ifc->address;
+
+ memset(&address, 0, sizeof(address));
+ address.family = p->family;
+ address.prefix = p->u.prefix6;
+ address.prefixlen = p->prefixlen;
+ apply_mask_ipv6(&address);
+
+ ripng_redistribute_delete(ripng, ZEBRA_ROUTE_CONNECT,
+ RIPNG_ROUTE_INTERFACE, &address,
+ ifc->ifp->ifindex);
+}
+
+int ripng_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_INET6) {
+ if (IS_RIPNG_DEBUG_ZEBRA)
+ zlog_debug(
+ "RIPng connected address %pFX delete",
+ p);
+
+ /* Check whether this prefix needs to be removed. */
+ ripng_apply_address_del(ifc);
+ }
+ connected_free(&ifc);
+ }
+
+ return 0;
+}
+
+/* Lookup RIPng enable network. */
+/* Check whether the interface has at least a connected prefix that
+ * is within the ripng->enable_network table. */
+static int ripng_enable_network_lookup_if(struct interface *ifp)
+{
+ struct ripng_interface *ri = ifp->info;
+ struct ripng *ripng = ri->ripng;
+ struct listnode *node;
+ struct connected *connected;
+ struct prefix_ipv6 address;
+
+ if (!ripng)
+ return -1;
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) {
+ struct prefix *p;
+ struct agg_node *n;
+
+ p = connected->address;
+
+ if (p->family == AF_INET6) {
+ address.family = AF_INET6;
+ address.prefix = p->u.prefix6;
+ address.prefixlen = IPV6_MAX_BITLEN;
+
+ n = agg_node_match(ripng->enable_network,
+ (struct prefix *)&address);
+ if (n) {
+ agg_unlock_node(n);
+ return 1;
+ }
+ }
+ }
+ return -1;
+}
+
+/* Check whether connected is within the ripng->enable_network table. */
+static int ripng_enable_network_lookup2(struct connected *connected)
+{
+ struct ripng_interface *ri = connected->ifp->info;
+ struct ripng *ripng = ri->ripng;
+ struct prefix_ipv6 address;
+ struct prefix *p;
+
+ if (!ripng)
+ return -1;
+
+ p = connected->address;
+
+ if (p->family == AF_INET6) {
+ struct agg_node *node;
+
+ address.family = p->family;
+ address.prefix = p->u.prefix6;
+ address.prefixlen = IPV6_MAX_BITLEN;
+
+ /* LPM on p->family, p->u.prefix6/IPV6_MAX_BITLEN within
+ * ripng->enable_network */
+ node = agg_node_match(ripng->enable_network,
+ (struct prefix *)&address);
+
+ if (node) {
+ agg_unlock_node(node);
+ return 1;
+ }
+ }
+
+ return -1;
+}
+
+/* Add RIPng enable network. */
+int ripng_enable_network_add(struct ripng *ripng, struct prefix *p)
+{
+ struct agg_node *node;
+
+ node = agg_node_get(ripng->enable_network, p);
+
+ if (node->info) {
+ agg_unlock_node(node);
+ return NB_ERR_INCONSISTENCY;
+ } else
+ node->info = (void *)1;
+
+ /* XXX: One should find a better solution than a generic one */
+ ripng_enable_apply_all(ripng);
+
+ return NB_OK;
+}
+
+/* Delete RIPng enable network. */
+int ripng_enable_network_delete(struct ripng *ripng, struct prefix *p)
+{
+ struct agg_node *node;
+
+ node = agg_node_lookup(ripng->enable_network, p);
+ if (node) {
+ node->info = NULL;
+
+ /* Unlock info lock. */
+ agg_unlock_node(node);
+
+ /* Unlock lookup lock. */
+ agg_unlock_node(node);
+
+ return NB_OK;
+ }
+
+ return NB_ERR_INCONSISTENCY;
+}
+
+/* Lookup function. */
+static int ripng_enable_if_lookup(struct ripng *ripng, const char *ifname)
+{
+ unsigned int i;
+ char *str;
+
+ if (!ripng)
+ return -1;
+
+ for (i = 0; i < vector_active(ripng->enable_if); i++)
+ if ((str = vector_slot(ripng->enable_if, i)) != NULL)
+ if (strcmp(str, ifname) == 0)
+ return i;
+ return -1;
+}
+
+int ripng_enable_if_add(struct ripng *ripng, const char *ifname)
+{
+ int ret;
+
+ ret = ripng_enable_if_lookup(ripng, ifname);
+ if (ret >= 0)
+ return NB_ERR_INCONSISTENCY;
+
+ vector_set(ripng->enable_if, strdup(ifname));
+
+ ripng_enable_apply_all(ripng);
+
+ return NB_OK;
+}
+
+int ripng_enable_if_delete(struct ripng *ripng, const char *ifname)
+{
+ int index;
+ char *str;
+
+ index = ripng_enable_if_lookup(ripng, ifname);
+ if (index < 0)
+ return NB_ERR_INCONSISTENCY;
+
+ str = vector_slot(ripng->enable_if, index);
+ free(str);
+ vector_unset(ripng->enable_if, index);
+
+ ripng_enable_apply_all(ripng);
+
+ return NB_OK;
+}
+
+/* Wake up interface. */
+static void ripng_interface_wakeup(struct event *t)
+{
+ struct interface *ifp;
+ struct ripng_interface *ri;
+
+ /* Get interface. */
+ ifp = EVENT_ARG(t);
+
+ ri = ifp->info;
+
+ /* Join to multicast group. */
+ if (ripng_multicast_join(ifp, ri->ripng->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. */
+ ripng_request(ifp);
+}
+
+static void ripng_connect_set(struct interface *ifp, int set)
+{
+ struct ripng_interface *ri = ifp->info;
+ struct ripng *ripng = ri->ripng;
+ struct listnode *node, *nnode;
+ struct connected *connected;
+ struct prefix_ipv6 address;
+
+ for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, connected)) {
+ struct prefix *p;
+ p = connected->address;
+
+ if (p->family != AF_INET6)
+ continue;
+
+ address.family = AF_INET6;
+ address.prefix = p->u.prefix6;
+ address.prefixlen = p->prefixlen;
+ apply_mask_ipv6(&address);
+
+ if (set) {
+ /* Check once more whether this prefix is within a
+ * "network IF_OR_PREF" one */
+ if ((ripng_enable_if_lookup(
+ ripng, connected->ifp->name) >= 0) ||
+ (ripng_enable_network_lookup2(connected) >= 0))
+ ripng_redistribute_add(
+ ripng, ZEBRA_ROUTE_CONNECT,
+ RIPNG_ROUTE_INTERFACE, &address,
+ connected->ifp->ifindex, NULL, 0);
+ } else {
+ ripng_redistribute_delete(ripng, ZEBRA_ROUTE_CONNECT,
+ RIPNG_ROUTE_INTERFACE,
+ &address,
+ connected->ifp->ifindex);
+ if (ripng_redistribute_check(ripng,
+ ZEBRA_ROUTE_CONNECT))
+ ripng_redistribute_add(
+ ripng, ZEBRA_ROUTE_CONNECT,
+ RIPNG_ROUTE_REDISTRIBUTE, &address,
+ connected->ifp->ifindex, NULL, 0);
+ }
+ }
+}
+
+/* Check RIPng is enabed on this interface. */
+void ripng_enable_apply(struct interface *ifp)
+{
+ int ret;
+ struct ripng_interface *ri = NULL;
+
+ /* Check interface. */
+ if (!if_is_up(ifp))
+ return;
+
+ ri = ifp->info;
+
+ /* Is this interface a candidate for RIPng ? */
+ ret = ripng_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 = ripng_enable_if_lookup(ri->ripng, ifp->name);
+ if (ret >= 0)
+ ri->enable_interface = 1;
+ else
+ ri->enable_interface = 0;
+
+ /* any candidate interface MUST have a link-local IPv6 address */
+ if ((!ripng_if_ipv6_lladdress_check(ifp))
+ && (ri->enable_network || ri->enable_interface)) {
+ ri->enable_network = 0;
+ ri->enable_interface = 0;
+ zlog_warn("Interface %s does not have any link-local address",
+ ifp->name);
+ }
+
+ /* Update running status of the interface. */
+ if (ri->enable_network || ri->enable_interface) {
+ zlog_info("RIPng INTERFACE ON %s", ifp->name);
+
+ /* Add interface wake up thread. */
+ event_add_timer(master, ripng_interface_wakeup, ifp, 1,
+ &ri->t_wakeup);
+
+ ripng_connect_set(ifp, 1);
+ } else {
+ if (ri->running) {
+ /* Might as well clean up the route table as well
+ * ripng_if_down sets to 0 ri->running, and displays
+ *"turn off %s"
+ **/
+ ripng_if_down(ifp);
+
+ ripng_connect_set(ifp, 0);
+ }
+ }
+}
+
+/* Set distribute list to all interfaces. */
+static void ripng_enable_apply_all(struct ripng *ripng)
+{
+ struct interface *ifp;
+
+ FOR_ALL_INTERFACES (ripng->vrf, ifp)
+ ripng_enable_apply(ifp);
+}
+
+/* Clear all network and neighbor configuration */
+void ripng_clean_network(struct ripng *ripng)
+{
+ unsigned int i;
+ char *str;
+ struct agg_node *rn;
+
+ /* ripng->enable_network */
+ for (rn = agg_route_top(ripng->enable_network); rn;
+ rn = agg_route_next(rn))
+ if (rn->info) {
+ rn->info = NULL;
+ agg_unlock_node(rn);
+ }
+
+ /* ripng->enable_if */
+ for (i = 0; i < vector_active(ripng->enable_if); i++)
+ if ((str = vector_slot(ripng->enable_if, i)) != NULL) {
+ free(str);
+ vector_slot(ripng->enable_if, i) = NULL;
+ }
+}
+
+/* Utility function for looking up passive interface settings. */
+static int ripng_passive_interface_lookup(struct ripng *ripng,
+ const char *ifname)
+{
+ unsigned int i;
+ char *str;
+
+ for (i = 0; i < vector_active(ripng->passive_interface); i++)
+ if ((str = vector_slot(ripng->passive_interface, i)) != NULL)
+ if (strcmp(str, ifname) == 0)
+ return i;
+ return -1;
+}
+
+void ripng_passive_interface_apply(struct interface *ifp)
+{
+ int ret;
+ struct ripng_interface *ri;
+ struct ripng *ripng;
+
+ ri = ifp->info;
+ ripng = ri->ripng;
+ if (!ripng)
+ return;
+
+ ret = ripng_passive_interface_lookup(ripng, ifp->name);
+ if (ret < 0)
+ ri->passive = 0;
+ else
+ ri->passive = 1;
+}
+
+static void ripng_passive_interface_apply_all(struct ripng *ripng)
+{
+ struct interface *ifp;
+
+ FOR_ALL_INTERFACES (ripng->vrf, ifp)
+ ripng_passive_interface_apply(ifp);
+}
+
+/* Passive interface. */
+int ripng_passive_interface_set(struct ripng *ripng, const char *ifname)
+{
+ if (ripng_passive_interface_lookup(ripng, ifname) >= 0)
+ return NB_ERR_INCONSISTENCY;
+
+ vector_set(ripng->passive_interface, strdup(ifname));
+
+ ripng_passive_interface_apply_all(ripng);
+
+ return NB_OK;
+}
+
+int ripng_passive_interface_unset(struct ripng *ripng, const char *ifname)
+{
+ int i;
+ char *str;
+
+ i = ripng_passive_interface_lookup(ripng, ifname);
+ if (i < 0)
+ return NB_ERR_INCONSISTENCY;
+
+ str = vector_slot(ripng->passive_interface, i);
+ free(str);
+ vector_unset(ripng->passive_interface, i);
+
+ ripng_passive_interface_apply_all(ripng);
+
+ return NB_OK;
+}
+
+/* Free all configured RIP passive-interface settings. */
+void ripng_passive_interface_clean(struct ripng *ripng)
+{
+ unsigned int i;
+ char *str;
+
+ for (i = 0; i < vector_active(ripng->passive_interface); i++)
+ if ((str = vector_slot(ripng->passive_interface, i)) != NULL) {
+ free(str);
+ vector_slot(ripng->passive_interface, i) = NULL;
+ }
+ ripng_passive_interface_apply_all(ripng);
+}
+
+/* Write RIPng enable network and interface to the vty. */
+int ripng_network_write(struct vty *vty, struct ripng *ripng)
+{
+ unsigned int i;
+ const char *ifname;
+ struct agg_node *node;
+
+ /* Write enable network. */
+ for (node = agg_route_top(ripng->enable_network); node;
+ node = agg_route_next(node))
+ if (node->info)
+ vty_out(vty, " %pRN\n", node);
+
+ /* Write enable interface. */
+ for (i = 0; i < vector_active(ripng->enable_if); i++)
+ if ((ifname = vector_slot(ripng->enable_if, i)) != NULL)
+ vty_out(vty, " %s\n", ifname);
+
+ return 0;
+}
+
+static struct ripng_interface *ri_new(void)
+{
+ struct ripng_interface *ri;
+
+ ri = XCALLOC(MTYPE_RIPNG_IF, sizeof(struct ripng_interface));
+
+ /* 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", RIPNG_IFACE);
+
+ return ri;
+}
+
+void ripng_interface_sync(struct interface *ifp)
+{
+ struct ripng_interface *ri;
+
+ ri = ifp->info;
+ if (ri)
+ ri->ripng = ifp->vrf->info;
+}
+
+static int ripng_if_new_hook(struct interface *ifp)
+{
+ ifp->info = ri_new();
+ ripng_interface_sync(ifp);
+
+ return 0;
+}
+
+/* Called when interface structure deleted. */
+static int ripng_if_delete_hook(struct interface *ifp)
+{
+ XFREE(MTYPE_RIPNG_IF, ifp->info);
+ return 0;
+}
+
+/* Initialization of interface. */
+void ripng_if_init(void)
+{
+ /* Interface initialize. */
+ hook_register_prio(if_add, 0, ripng_if_new_hook);
+ hook_register_prio(if_del, 0, ripng_if_delete_hook);
+
+ /* Install interface node. */
+ if_cmd_init_default();
+ if_zapi_callbacks(ripng_ifp_create, ripng_ifp_up,
+ ripng_ifp_down, ripng_ifp_destroy);
+}
diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c
new file mode 100644
index 0000000..9933dae
--- /dev/null
+++ b/ripngd/ripng_main.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RIPngd main routine.
+ * Copyright (C) 1998, 1999 Kunihiro Ishiguro
+ */
+
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "getopt.h"
+#include "vector.h"
+#include "vty.h"
+#include "command.h"
+#include "memory.h"
+#include "frrevent.h"
+#include "log.h"
+#include "prefix.h"
+#include "if.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "vrf.h"
+#include "if_rmap.h"
+#include "libfrr.h"
+#include "routemap.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_nb.h"
+
+/* RIPngd options. */
+struct option longopts[] = {{0}};
+
+/* ripngd 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 ripngd_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 ripngd_di;
+
+/* SIGHUP handler. */
+static void sighup(void)
+{
+ zlog_info("SIGHUP received");
+
+ /* Reload config file. */
+ vty_read_config(NULL, ripngd_di.config_file, config_default);
+}
+
+/* SIGINT handler. */
+static void sigint(void)
+{
+ zlog_notice("Terminating on signal");
+
+ ripng_vrf_terminate();
+ if_rmap_terminate();
+ ripng_zebra_stop();
+ frr_fini();
+ exit(0);
+}
+
+/* SIGUSR1 handler. */
+static void sigusr1(void)
+{
+ zlog_rotate();
+}
+
+struct frr_signal_t ripng_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 ripngd_yang_modules[] = {
+ &frr_filter_info,
+ &frr_interface_info,
+ &frr_ripngd_info,
+ &frr_route_map_info,
+ &frr_vrf_info,
+};
+
+FRR_DAEMON_INFO(ripngd, RIPNG, .vty_port = RIPNG_VTY_PORT,
+
+ .proghelp = "Implementation of the RIPng routing protocol.",
+
+ .signals = ripng_signals,
+ .n_signals = array_size(ripng_signals),
+
+ .privs = &ripngd_privs,
+
+ .yang_modules = ripngd_yang_modules,
+ .n_yang_modules = array_size(ripngd_yang_modules),
+);
+
+#define DEPRECATED_OPTIONS ""
+
+/* RIPngd main routine. */
+int main(int argc, char **argv)
+{
+ frr_preinit(&ripngd_di, argc, argv);
+
+ frr_opt_add("" DEPRECATED_OPTIONS, longopts, "");
+
+ 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);
+ }
+ }
+
+ master = frr_init();
+
+ /* Library inits. */
+ ripng_vrf_init();
+
+ /* RIPngd inits. */
+ ripng_init();
+ ripng_cli_init();
+ zebra_init(master);
+
+ frr_config_fork();
+ frr_run(master);
+
+ /* Not reached. */
+ return 0;
+}
diff --git a/ripngd/ripng_nb.c b/ripngd/ripng_nb.c
new file mode 100644
index 0000000..1c6d719
--- /dev/null
+++ b/ripngd/ripng_nb.c
@@ -0,0 +1,262 @@
+// 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 "ripngd/ripng_nb.h"
+#include "lib/if_rmap.h"
+
+/* clang-format off */
+const struct frr_yang_module_info frr_ripngd_info = {
+ .name = "frr-ripngd",
+ .nodes = {
+ {
+ .xpath = "/frr-ripngd:ripngd/instance",
+ .cbs = {
+ .cli_show = cli_show_router_ripng,
+ .create = ripngd_instance_create,
+ .destroy = ripngd_instance_destroy,
+ .get_keys = ripngd_instance_get_keys,
+ .get_next = ripngd_instance_get_next,
+ .lookup_entry = ripngd_instance_lookup_entry,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/allow-ecmp",
+ .cbs = {
+ .cli_show = cli_show_ripng_allow_ecmp,
+ .modify = ripngd_instance_allow_ecmp_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/default-information-originate",
+ .cbs = {
+ .cli_show = cli_show_ripng_default_information_originate,
+ .modify = ripngd_instance_default_information_originate_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/default-metric",
+ .cbs = {
+ .cli_show = cli_show_ripng_default_metric,
+ .modify = ripngd_instance_default_metric_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/network",
+ .cbs = {
+ .cli_show = cli_show_ripng_network_prefix,
+ .create = ripngd_instance_network_create,
+ .destroy = ripngd_instance_network_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/interface",
+ .cbs = {
+ .cli_show = cli_show_ripng_network_interface,
+ .create = ripngd_instance_interface_create,
+ .destroy = ripngd_instance_interface_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/offset-list",
+ .cbs = {
+ .cli_show = cli_show_ripng_offset_list,
+ .create = ripngd_instance_offset_list_create,
+ .destroy = ripngd_instance_offset_list_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/offset-list/access-list",
+ .cbs = {
+ .modify = ripngd_instance_offset_list_access_list_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/offset-list/metric",
+ .cbs = {
+ .modify = ripngd_instance_offset_list_metric_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/passive-interface",
+ .cbs = {
+ .cli_show = cli_show_ripng_passive_interface,
+ .create = ripngd_instance_passive_interface_create,
+ .destroy = ripngd_instance_passive_interface_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/redistribute",
+ .cbs = {
+ .apply_finish = ripngd_instance_redistribute_apply_finish,
+ .cli_show = cli_show_ripng_redistribute,
+ .create = ripngd_instance_redistribute_create,
+ .destroy = ripngd_instance_redistribute_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/redistribute/route-map",
+ .cbs = {
+ .destroy = ripngd_instance_redistribute_route_map_destroy,
+ .modify = ripngd_instance_redistribute_route_map_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/redistribute/metric",
+ .cbs = {
+ .destroy = ripngd_instance_redistribute_metric_destroy,
+ .modify = ripngd_instance_redistribute_metric_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/if-route-maps/if-route-map",
+ .cbs = {
+ .create = ripngd_instance_if_route_maps_if_route_map_create,
+ .destroy = ripngd_instance_if_route_maps_if_route_map_destroy,
+ .cli_show = cli_show_if_route_map,
+ }
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/if-route-maps/if-route-map/in-route-map",
+ .cbs = {
+ .modify = ripngd_instance_if_route_maps_if_route_map_in_route_map_modify,
+ .destroy = ripngd_instance_if_route_maps_if_route_map_in_route_map_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/if-route-maps/if-route-map/out-route-map",
+ .cbs = {
+ .modify = ripngd_instance_if_route_maps_if_route_map_out_route_map_modify,
+ .destroy = ripngd_instance_if_route_maps_if_route_map_out_route_map_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/static-route",
+ .cbs = {
+ .cli_show = cli_show_ripng_route,
+ .create = ripngd_instance_static_route_create,
+ .destroy = ripngd_instance_static_route_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/aggregate-address",
+ .cbs = {
+ .cli_show = cli_show_ripng_aggregate_address,
+ .create = ripngd_instance_aggregate_address_create,
+ .destroy = ripngd_instance_aggregate_address_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/timers",
+ .cbs = {
+ .apply_finish = ripngd_instance_timers_apply_finish,
+ .cli_show = cli_show_ripng_timers,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/timers/flush-interval",
+ .cbs = {
+ .modify = ripngd_instance_timers_flush_interval_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/timers/holddown-interval",
+ .cbs = {
+ .modify = ripngd_instance_timers_holddown_interval_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/timers/update-interval",
+ .cbs = {
+ .modify = ripngd_instance_timers_update_interval_modify,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor",
+ .cbs = {
+ .get_keys = ripngd_instance_state_neighbors_neighbor_get_keys,
+ .get_next = ripngd_instance_state_neighbors_neighbor_get_next,
+ .lookup_entry = ripngd_instance_state_neighbors_neighbor_lookup_entry,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor/address",
+ .cbs = {
+ .get_elem = ripngd_instance_state_neighbors_neighbor_address_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor/last-update",
+ .cbs = {
+ .get_elem = ripngd_instance_state_neighbors_neighbor_last_update_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor/bad-packets-rcvd",
+ .cbs = {
+ .get_elem = ripngd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor/bad-routes-rcvd",
+ .cbs = {
+ .get_elem = ripngd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/state/routes/route",
+ .cbs = {
+ .get_keys = ripngd_instance_state_routes_route_get_keys,
+ .get_next = ripngd_instance_state_routes_route_get_next,
+ .lookup_entry = ripngd_instance_state_routes_route_lookup_entry,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/state/routes/route/prefix",
+ .cbs = {
+ .get_elem = ripngd_instance_state_routes_route_prefix_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/state/routes/route/next-hop",
+ .cbs = {
+ .get_elem = ripngd_instance_state_routes_route_next_hop_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/state/routes/route/interface",
+ .cbs = {
+ .get_elem = ripngd_instance_state_routes_route_interface_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:ripngd/instance/state/routes/route/metric",
+ .cbs = {
+ .get_elem = ripngd_instance_state_routes_route_metric_get_elem,
+ },
+ },
+ {
+ .xpath = "/frr-ripngd:clear-ripng-route",
+ .cbs = {
+ .rpc = clear_ripng_route_rpc,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-ripngd:ripng/split-horizon",
+ .cbs = {
+ .cli_show = cli_show_ipv6_ripng_split_horizon,
+ .modify = lib_interface_ripng_split_horizon_modify,
+ },
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
diff --git a/ripngd/ripng_nb.h b/ripngd/ripng_nb.h
new file mode 100644
index 0000000..1c0e63c
--- /dev/null
+++ b/ripngd/ripng_nb.h
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ */
+
+#ifndef _FRR_RIPNG_NB_H_
+#define _FRR_RIPNG_NB_H_
+
+extern const struct frr_yang_module_info frr_ripngd_info;
+
+/* Mandatory callbacks. */
+int ripngd_instance_create(struct nb_cb_create_args *args);
+int ripngd_instance_destroy(struct nb_cb_destroy_args *args);
+const void *ripngd_instance_get_next(struct nb_cb_get_next_args *args);
+int ripngd_instance_get_keys(struct nb_cb_get_keys_args *args);
+const void *ripngd_instance_lookup_entry(struct nb_cb_lookup_entry_args *args);
+int ripngd_instance_allow_ecmp_modify(struct nb_cb_modify_args *args);
+int ripngd_instance_default_information_originate_modify(
+ struct nb_cb_modify_args *args);
+int ripngd_instance_default_metric_modify(struct nb_cb_modify_args *args);
+int ripngd_instance_network_create(struct nb_cb_create_args *args);
+int ripngd_instance_network_destroy(struct nb_cb_destroy_args *args);
+int ripngd_instance_interface_create(struct nb_cb_create_args *args);
+int ripngd_instance_interface_destroy(struct nb_cb_destroy_args *args);
+int ripngd_instance_offset_list_create(struct nb_cb_create_args *args);
+int ripngd_instance_offset_list_destroy(struct nb_cb_destroy_args *args);
+int ripngd_instance_offset_list_access_list_modify(
+ struct nb_cb_modify_args *args);
+int ripngd_instance_offset_list_metric_modify(struct nb_cb_modify_args *args);
+int ripngd_instance_passive_interface_create(struct nb_cb_create_args *args);
+int ripngd_instance_passive_interface_destroy(struct nb_cb_destroy_args *args);
+int ripngd_instance_redistribute_create(struct nb_cb_create_args *args);
+int ripngd_instance_redistribute_destroy(struct nb_cb_destroy_args *args);
+int ripngd_instance_redistribute_route_map_modify(
+ struct nb_cb_modify_args *args);
+int ripngd_instance_redistribute_route_map_destroy(
+ struct nb_cb_destroy_args *args);
+int ripngd_instance_redistribute_metric_modify(struct nb_cb_modify_args *args);
+int ripngd_instance_redistribute_metric_destroy(
+ struct nb_cb_destroy_args *args);
+int ripngd_instance_if_route_maps_if_route_map_create(
+ struct nb_cb_create_args *args);
+int ripngd_instance_if_route_maps_if_route_map_destroy(
+ struct nb_cb_destroy_args *args);
+int ripngd_instance_if_route_maps_if_route_map_in_route_map_modify(
+ struct nb_cb_modify_args *args);
+int ripngd_instance_if_route_maps_if_route_map_in_route_map_destroy(
+ struct nb_cb_destroy_args *args);
+int ripngd_instance_if_route_maps_if_route_map_out_route_map_modify(
+ struct nb_cb_modify_args *args);
+int ripngd_instance_if_route_maps_if_route_map_out_route_map_destroy(
+ struct nb_cb_destroy_args *args);
+int ripngd_instance_static_route_create(struct nb_cb_create_args *args);
+int ripngd_instance_static_route_destroy(struct nb_cb_destroy_args *args);
+int ripngd_instance_aggregate_address_create(struct nb_cb_create_args *args);
+int ripngd_instance_aggregate_address_destroy(struct nb_cb_destroy_args *args);
+int ripngd_instance_timers_flush_interval_modify(
+ struct nb_cb_modify_args *args);
+int ripngd_instance_timers_holddown_interval_modify(
+ struct nb_cb_modify_args *args);
+int ripngd_instance_timers_update_interval_modify(
+ struct nb_cb_modify_args *args);
+const void *ripngd_instance_state_neighbors_neighbor_get_next(
+ struct nb_cb_get_next_args *args);
+int ripngd_instance_state_neighbors_neighbor_get_keys(
+ struct nb_cb_get_keys_args *args);
+const void *ripngd_instance_state_neighbors_neighbor_lookup_entry(
+ struct nb_cb_lookup_entry_args *args);
+struct yang_data *ripngd_instance_state_neighbors_neighbor_address_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *ripngd_instance_state_neighbors_neighbor_last_update_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *
+ripngd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *
+ripngd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem(
+ struct nb_cb_get_elem_args *args);
+const void *
+ripngd_instance_state_routes_route_get_next(struct nb_cb_get_next_args *args);
+int ripngd_instance_state_routes_route_get_keys(
+ struct nb_cb_get_keys_args *args);
+const void *ripngd_instance_state_routes_route_lookup_entry(
+ struct nb_cb_lookup_entry_args *args);
+struct yang_data *ripngd_instance_state_routes_route_prefix_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *ripngd_instance_state_routes_route_next_hop_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *ripngd_instance_state_routes_route_interface_get_elem(
+ struct nb_cb_get_elem_args *args);
+struct yang_data *ripngd_instance_state_routes_route_metric_get_elem(
+ struct nb_cb_get_elem_args *args);
+int clear_ripng_route_rpc(struct nb_cb_rpc_args *args);
+int lib_interface_ripng_split_horizon_modify(struct nb_cb_modify_args *args);
+
+/* Optional 'apply_finish' callbacks. */
+void ripngd_instance_redistribute_apply_finish(
+ struct nb_cb_apply_finish_args *args);
+void ripngd_instance_timers_apply_finish(struct nb_cb_apply_finish_args *args);
+
+/* Optional 'cli_show' callbacks. */
+void cli_show_router_ripng(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_allow_ecmp(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_default_information_originate(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_default_metric(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_network_prefix(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_network_interface(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_offset_list(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_passive_interface(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_redistribute(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_route(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_aggregate_address(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ripng_timers(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ipv6_ripng_split_horizon(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+
+#endif /* _FRR_RIPNG_NB_H_ */
diff --git a/ripngd/ripng_nb_config.c b/ripngd/ripng_nb_config.c
new file mode 100644
index 0000000..0b1bd68
--- /dev/null
+++ b/ripngd/ripng_nb_config.c
@@ -0,0 +1,754 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ * 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 "agg_table.h"
+#include "northbound.h"
+#include "libfrr.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_nb.h"
+#include "ripngd/ripng_debug.h"
+#include "ripngd/ripng_route.h"
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance
+ */
+int ripngd_instance_create(struct nb_cb_create_args *args)
+{
+ struct ripng *ripng;
+ 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 RIPng socket only if the VRF is enabled, otherwise
+ * create a disabled RIPng 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 = ripng_make_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;
+
+ ripng = ripng_create(vrf_name, vrf, socket);
+ nb_running_set_entry(args->dnode, ripng);
+ break;
+ }
+
+ return NB_OK;
+}
+
+int ripngd_instance_destroy(struct nb_cb_destroy_args *args)
+{
+ struct ripng *ripng;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_unset_entry(args->dnode);
+ ripng_clean(ripng);
+
+ return NB_OK;
+}
+
+const void *ripngd_instance_get_next(struct nb_cb_get_next_args *args)
+{
+ struct ripng *ripng = (struct ripng *)args->list_entry;
+
+ if (args->list_entry == NULL)
+ ripng = RB_MIN(ripng_instance_head, &ripng_instances);
+ else
+ ripng = RB_NEXT(ripng_instance_head, ripng);
+
+ return ripng;
+}
+
+int ripngd_instance_get_keys(struct nb_cb_get_keys_args *args)
+{
+ const struct ripng *ripng = args->list_entry;
+
+ args->keys->num = 1;
+ strlcpy(args->keys->key[0], ripng->vrf_name,
+ sizeof(args->keys->key[0]));
+
+ return NB_OK;
+}
+
+const void *ripngd_instance_lookup_entry(struct nb_cb_lookup_entry_args *args)
+{
+ const char *vrf_name = args->keys->key[0];
+
+ return ripng_lookup_by_vrf_name(vrf_name);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/allow-ecmp
+ */
+int ripngd_instance_allow_ecmp_modify(struct nb_cb_modify_args *args)
+{
+ struct ripng *ripng;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ ripng->ecmp =
+ MIN(yang_dnode_get_uint8(args->dnode, NULL), zebra_ecmp_count);
+ if (!ripng->ecmp) {
+ ripng_ecmp_disable(ripng);
+ return NB_OK;
+ }
+
+ ripng_ecmp_change(ripng);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/default-information-originate
+ */
+int ripngd_instance_default_information_originate_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct ripng *ripng;
+ bool default_information;
+ struct prefix_ipv6 p;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ default_information = yang_dnode_get_bool(args->dnode, NULL);
+
+ (void)str2prefix_ipv6("::/0", &p);
+ if (default_information) {
+ ripng_redistribute_add(ripng, ZEBRA_ROUTE_RIPNG,
+ RIPNG_ROUTE_DEFAULT, &p, 0, NULL, 0);
+ } else {
+ ripng_redistribute_delete(ripng, ZEBRA_ROUTE_RIPNG,
+ RIPNG_ROUTE_DEFAULT, &p, 0);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/default-metric
+ */
+int ripngd_instance_default_metric_modify(struct nb_cb_modify_args *args)
+{
+ struct ripng *ripng;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ ripng->default_metric = yang_dnode_get_uint8(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/network
+ */
+int ripngd_instance_network_create(struct nb_cb_create_args *args)
+{
+ struct ripng *ripng;
+ struct prefix p;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ yang_dnode_get_ipv6p(&p, args->dnode, NULL);
+ apply_mask_ipv6((struct prefix_ipv6 *)&p);
+
+ return ripng_enable_network_add(ripng, &p);
+}
+
+int ripngd_instance_network_destroy(struct nb_cb_destroy_args *args)
+{
+ struct ripng *ripng;
+ struct prefix p;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ yang_dnode_get_ipv6p(&p, args->dnode, NULL);
+ apply_mask_ipv6((struct prefix_ipv6 *)&p);
+
+ return ripng_enable_network_delete(ripng, &p);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/interface
+ */
+int ripngd_instance_interface_create(struct nb_cb_create_args *args)
+{
+ struct ripng *ripng;
+ const char *ifname;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ ifname = yang_dnode_get_string(args->dnode, NULL);
+
+ return ripng_enable_if_add(ripng, ifname);
+}
+
+int ripngd_instance_interface_destroy(struct nb_cb_destroy_args *args)
+{
+ struct ripng *ripng;
+ const char *ifname;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ ifname = yang_dnode_get_string(args->dnode, NULL);
+
+ return ripng_enable_if_delete(ripng, ifname);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/offset-list
+ */
+int ripngd_instance_offset_list_create(struct nb_cb_create_args *args)
+{
+ struct ripng *ripng;
+ const char *ifname;
+ struct ripng_offset_list *offset;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ ifname = yang_dnode_get_string(args->dnode, "./interface");
+
+ offset = ripng_offset_list_new(ripng, ifname);
+ nb_running_set_entry(args->dnode, offset);
+
+ return NB_OK;
+}
+
+int ripngd_instance_offset_list_destroy(struct nb_cb_destroy_args *args)
+{
+ int direct;
+ struct ripng_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[RIPNG_OFFSET_LIST_IN].alist_name == NULL
+ && offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name == NULL)
+ ripng_offset_list_del(offset);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/offset-list/access-list
+ */
+int ripngd_instance_offset_list_access_list_modify(
+ struct nb_cb_modify_args *args)
+{
+ int direct;
+ struct ripng_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-ripngd:ripngd/instance/offset-list/metric
+ */
+int ripngd_instance_offset_list_metric_modify(struct nb_cb_modify_args *args)
+{
+ int direct;
+ uint8_t metric;
+ struct ripng_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-ripngd:ripngd/instance/passive-interface
+ */
+int ripngd_instance_passive_interface_create(struct nb_cb_create_args *args)
+{
+ struct ripng *ripng;
+ const char *ifname;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ ifname = yang_dnode_get_string(args->dnode, NULL);
+
+ return ripng_passive_interface_set(ripng, ifname);
+}
+
+int ripngd_instance_passive_interface_destroy(struct nb_cb_destroy_args *args)
+{
+ struct ripng *ripng;
+ const char *ifname;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ ifname = yang_dnode_get_string(args->dnode, NULL);
+
+ return ripng_passive_interface_unset(ripng, ifname);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/redistribute
+ */
+int ripngd_instance_redistribute_create(struct nb_cb_create_args *args)
+{
+ struct ripng *ripng;
+ int type;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_enum(args->dnode, "./protocol");
+
+ ripng->redist[type].enabled = true;
+
+ return NB_OK;
+}
+
+int ripngd_instance_redistribute_destroy(struct nb_cb_destroy_args *args)
+{
+ struct ripng *ripng;
+ int type;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_enum(args->dnode, "./protocol");
+
+ ripng->redist[type].enabled = false;
+ if (ripng->redist[type].route_map.name) {
+ free(ripng->redist[type].route_map.name);
+ ripng->redist[type].route_map.name = NULL;
+ ripng->redist[type].route_map.map = NULL;
+ }
+ ripng->redist[type].metric_config = false;
+ ripng->redist[type].metric = 0;
+
+ if (ripng->enabled)
+ ripng_redistribute_conf_delete(ripng, type);
+
+ return NB_OK;
+}
+
+void ripngd_instance_redistribute_apply_finish(
+ struct nb_cb_apply_finish_args *args)
+{
+ struct ripng *ripng;
+ int type;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_enum(args->dnode, "./protocol");
+
+ if (ripng->enabled)
+ ripng_redistribute_conf_update(ripng, type);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/redistribute/route-map
+ */
+int ripngd_instance_redistribute_route_map_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct ripng *ripng;
+ int type;
+ const char *rmap_name;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = 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 (ripng->redist[type].route_map.name)
+ free(ripng->redist[type].route_map.name);
+ ripng->redist[type].route_map.name = strdup(rmap_name);
+ ripng->redist[type].route_map.map = route_map_lookup_by_name(rmap_name);
+
+ return NB_OK;
+}
+
+int ripngd_instance_redistribute_route_map_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct ripng *ripng;
+ int type;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_enum(args->dnode, "../protocol");
+
+ free(ripng->redist[type].route_map.name);
+ ripng->redist[type].route_map.name = NULL;
+ ripng->redist[type].route_map.map = NULL;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/redistribute/metric
+ */
+int ripngd_instance_redistribute_metric_modify(struct nb_cb_modify_args *args)
+{
+ struct ripng *ripng;
+ int type;
+ uint8_t metric;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_enum(args->dnode, "../protocol");
+ metric = yang_dnode_get_uint8(args->dnode, NULL);
+
+ ripng->redist[type].metric_config = true;
+ ripng->redist[type].metric = metric;
+
+ return NB_OK;
+}
+
+int ripngd_instance_redistribute_metric_destroy(struct nb_cb_destroy_args *args)
+{
+ struct ripng *ripng;
+ int type;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_enum(args->dnode, "../protocol");
+
+ ripng->redist[type].metric_config = false;
+ ripng->redist[type].metric = 0;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/if-route-maps/if-route-map
+ */
+int ripngd_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 ripngd_instance_if_route_maps_if_route_map_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct ripng *ripng;
+
+ 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.
+ */
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ if_rmap_yang_destroy_cb(ripng->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 ripng *ripng = nb_running_get_entry(dnode, NULL, true);
+
+ if_rmap_yang_modify_cb(ripng->if_rmap_ctx, dnode, type, delete);
+}
+/*
+ * XPath: /frr-ripng:ripng/instance/if-route-maps/if-route-map/in-route-map
+ */
+int ripngd_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 ripngd_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-ripngd:ripngd/instance/if-route-maps/if-route-map/out-route-map
+ */
+int ripngd_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 ripngd_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-ripngd:ripngd/instance/static-route
+ */
+int ripngd_instance_static_route_create(struct nb_cb_create_args *args)
+{
+ struct ripng *ripng;
+ struct prefix_ipv6 p;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ yang_dnode_get_ipv6p(&p, args->dnode, NULL);
+ apply_mask_ipv6(&p);
+
+ ripng_redistribute_add(ripng, ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC, &p,
+ 0, NULL, 0);
+
+ return NB_OK;
+}
+
+int ripngd_instance_static_route_destroy(struct nb_cb_destroy_args *args)
+{
+ struct ripng *ripng;
+ struct prefix_ipv6 p;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ yang_dnode_get_ipv6p(&p, args->dnode, NULL);
+ apply_mask_ipv6(&p);
+
+ ripng_redistribute_delete(ripng, ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC,
+ &p, 0);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/aggregate-address
+ */
+int ripngd_instance_aggregate_address_create(struct nb_cb_create_args *args)
+{
+ struct ripng *ripng;
+ struct prefix_ipv6 p;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ yang_dnode_get_ipv6p(&p, args->dnode, NULL);
+ apply_mask_ipv6(&p);
+
+ ripng_aggregate_add(ripng, (struct prefix *)&p);
+
+ return NB_OK;
+}
+
+int ripngd_instance_aggregate_address_destroy(struct nb_cb_destroy_args *args)
+{
+ struct ripng *ripng;
+ struct prefix_ipv6 p;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ yang_dnode_get_ipv6p(&p, args->dnode, NULL);
+ apply_mask_ipv6(&p);
+
+ ripng_aggregate_delete(ripng, (struct prefix *)&p);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/timers
+ */
+void ripngd_instance_timers_apply_finish(struct nb_cb_apply_finish_args *args)
+{
+ struct ripng *ripng;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+
+ /* Reset update timer thread. */
+ ripng_event(ripng, RIPNG_UPDATE_EVENT, 0);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/timers/flush-interval
+ */
+int ripngd_instance_timers_flush_interval_modify(struct nb_cb_modify_args *args)
+{
+ struct ripng *ripng;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ ripng->garbage_time = yang_dnode_get_uint16(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/timers/holddown-interval
+ */
+int ripngd_instance_timers_holddown_interval_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct ripng *ripng;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ ripng->timeout_time = yang_dnode_get_uint16(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/timers/update-interval
+ */
+int ripngd_instance_timers_update_interval_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct ripng *ripng;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ripng = nb_running_get_entry(args->dnode, NULL, true);
+ ripng->update_time = yang_dnode_get_uint16(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripngd:ripng/split-horizon
+ */
+int lib_interface_ripng_split_horizon_modify(struct nb_cb_modify_args *args)
+{
+ struct interface *ifp;
+ struct ripng_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;
+}
diff --git a/ripngd/ripng_nb_rpcs.c b/ripngd/ripng_nb_rpcs.c
new file mode 100644
index 0000000..b23572d
--- /dev/null
+++ b/ripngd/ripng_nb_rpcs.c
@@ -0,0 +1,94 @@
+// 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 "agg_table.h"
+#include "northbound.h"
+#include "libfrr.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_nb.h"
+#include "ripngd/ripng_debug.h"
+#include "ripngd/ripng_route.h"
+
+/*
+ * XPath: /frr-ripngd:clear-ripng-route
+ */
+static void clear_ripng_route(struct ripng *ripng)
+{
+ struct agg_node *rp;
+
+ if (IS_RIPNG_DEBUG_EVENT)
+ zlog_debug("Clearing all RIPng routes (VRF %s)",
+ ripng->vrf_name);
+
+ /* Clear received RIPng routes */
+ for (rp = agg_route_top(ripng->table); rp; rp = agg_route_next(rp)) {
+ struct list *list;
+ struct listnode *listnode;
+ struct ripng_info *rinfo;
+
+ list = rp->info;
+ if (list == NULL)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) {
+ if (!ripng_route_rte(rinfo))
+ continue;
+
+ if (CHECK_FLAG(rinfo->flags, RIPNG_RTF_FIB))
+ ripng_zebra_ipv6_delete(ripng, rp);
+ break;
+ }
+
+ if (rinfo) {
+ EVENT_OFF(rinfo->t_timeout);
+ EVENT_OFF(rinfo->t_garbage_collect);
+ listnode_delete(list, rinfo);
+ ripng_info_free(rinfo);
+ }
+
+ if (list_isempty(list)) {
+ list_delete(&list);
+ rp->info = NULL;
+ agg_unlock_node(rp);
+ }
+ }
+}
+
+int clear_ripng_route_rpc(struct nb_cb_rpc_args *args)
+{
+ struct ripng *ripng;
+ struct yang_data *yang_vrf;
+
+ yang_vrf = yang_data_list_find(args->input, "%s/%s", args->xpath,
+ "input/vrf");
+ if (yang_vrf) {
+ ripng = ripng_lookup_by_vrf_name(yang_vrf->value);
+ if (ripng)
+ clear_ripng_route(ripng);
+ } else {
+ struct vrf *vrf;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ ripng = vrf->info;
+ if (!ripng)
+ continue;
+
+ clear_ripng_route(ripng);
+ }
+ }
+
+ return NB_OK;
+}
diff --git a/ripngd/ripng_nb_state.c b/ripngd/ripng_nb_state.c
new file mode 100644
index 0000000..3f37df3
--- /dev/null
+++ b/ripngd/ripng_nb_state.c
@@ -0,0 +1,220 @@
+// 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 "agg_table.h"
+#include "northbound.h"
+#include "libfrr.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_nb.h"
+#include "ripngd/ripng_debug.h"
+#include "ripngd/ripng_route.h"
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor
+ */
+const void *ripngd_instance_state_neighbors_neighbor_get_next(
+ struct nb_cb_get_next_args *args)
+{
+ const struct ripng *ripng = args->parent_list_entry;
+ struct listnode *node;
+
+ if (args->list_entry == NULL)
+ node = listhead(ripng->peer_list);
+ else
+ node = listnextnode((struct listnode *)args->list_entry);
+
+ return node;
+}
+
+int ripngd_instance_state_neighbors_neighbor_get_keys(
+ struct nb_cb_get_keys_args *args)
+{
+ const struct listnode *node = args->list_entry;
+ const struct ripng_peer *peer = listgetdata(node);
+
+ args->keys->num = 1;
+ (void)inet_ntop(AF_INET6, &peer->addr, args->keys->key[0],
+ sizeof(args->keys->key[0]));
+
+ return NB_OK;
+}
+
+const void *ripngd_instance_state_neighbors_neighbor_lookup_entry(
+ struct nb_cb_lookup_entry_args *args)
+{
+ const struct ripng *ripng = args->parent_list_entry;
+ struct in6_addr address;
+ struct ripng_peer *peer;
+ struct listnode *node;
+
+ yang_str2ipv6(args->keys->key[0], &address);
+
+ for (ALL_LIST_ELEMENTS_RO(ripng->peer_list, node, peer)) {
+ if (IPV6_ADDR_SAME(&peer->addr, &address))
+ return node;
+ }
+
+ return NULL;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor/address
+ */
+struct yang_data *ripngd_instance_state_neighbors_neighbor_address_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct listnode *node = args->list_entry;
+ const struct ripng_peer *peer = listgetdata(node);
+
+ return yang_data_new_ipv6(args->xpath, &peer->addr);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor/last-update
+ */
+struct yang_data *ripngd_instance_state_neighbors_neighbor_last_update_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ /* TODO: yang:date-and-time is tricky */
+ return NULL;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor/bad-packets-rcvd
+ */
+struct yang_data *
+ripngd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct listnode *node = args->list_entry;
+ const struct ripng_peer *peer = listgetdata(node);
+
+ return yang_data_new_uint32(args->xpath, peer->recv_badpackets);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor/bad-routes-rcvd
+ */
+struct yang_data *
+ripngd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct listnode *node = args->list_entry;
+ const struct ripng_peer *peer = listgetdata(node);
+
+ return yang_data_new_uint32(args->xpath, peer->recv_badroutes);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/state/routes/route
+ */
+const void *
+ripngd_instance_state_routes_route_get_next(struct nb_cb_get_next_args *args)
+{
+ const struct ripng *ripng = args->parent_list_entry;
+ struct agg_node *rn;
+
+ if (args->list_entry == NULL)
+ rn = agg_route_top(ripng->table);
+ else
+ rn = agg_route_next((struct agg_node *)args->list_entry);
+ /* Optimization: skip empty route nodes. */
+ while (rn && rn->info == NULL)
+ rn = agg_route_next(rn);
+
+ return rn;
+}
+
+int ripngd_instance_state_routes_route_get_keys(
+ struct nb_cb_get_keys_args *args)
+{
+ const struct agg_node *rn = args->list_entry;
+
+ args->keys->num = 1;
+ (void)prefix2str(agg_node_get_prefix(rn), args->keys->key[0],
+ sizeof(args->keys->key[0]));
+
+ return NB_OK;
+}
+
+const void *ripngd_instance_state_routes_route_lookup_entry(
+ struct nb_cb_lookup_entry_args *args)
+{
+ const struct ripng *ripng = args->parent_list_entry;
+ struct prefix prefix;
+ struct agg_node *rn;
+
+ yang_str2ipv6p(args->keys->key[0], &prefix);
+
+ rn = agg_node_lookup(ripng->table, &prefix);
+ if (!rn || !rn->info)
+ return NULL;
+
+ agg_unlock_node(rn);
+
+ return rn;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/state/routes/route/prefix
+ */
+struct yang_data *ripngd_instance_state_routes_route_prefix_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct agg_node *rn = args->list_entry;
+ const struct ripng_info *rinfo = listnode_head(rn->info);
+
+ return yang_data_new_ipv6p(args->xpath, agg_node_get_prefix(rinfo->rp));
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/state/routes/route/next-hop
+ */
+struct yang_data *ripngd_instance_state_routes_route_next_hop_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct agg_node *rn = args->list_entry;
+ const struct ripng_info *rinfo = listnode_head(rn->info);
+
+ return yang_data_new_ipv6(args->xpath, &rinfo->nexthop);
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/state/routes/route/interface
+ */
+struct yang_data *ripngd_instance_state_routes_route_interface_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct agg_node *rn = args->list_entry;
+ const struct ripng_info *rinfo = listnode_head(rn->info);
+ const struct ripng *ripng = ripng_info_get_instance(rinfo);
+
+ return yang_data_new_string(
+ args->xpath,
+ ifindex2ifname(rinfo->ifindex, ripng->vrf->vrf_id));
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/state/routes/route/metric
+ */
+struct yang_data *ripngd_instance_state_routes_route_metric_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct agg_node *rn = args->list_entry;
+ const struct ripng_info *rinfo = listnode_head(rn->info);
+
+ return yang_data_new_uint8(args->xpath, rinfo->metric);
+}
diff --git a/ripngd/ripng_nexthop.c b/ripngd/ripng_nexthop.c
new file mode 100644
index 0000000..38e7ce2
--- /dev/null
+++ b/ripngd/ripng_nexthop.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RIPngd Zebra
+ * Copyright (C) 2002 6WIND <vincent.jardin@6wind.com>
+ */
+
+/* This file is required in order to support properly the RIPng nexthop
+ * feature.
+ */
+
+#include <zebra.h>
+
+/* For struct udphdr. */
+#include <netinet/udp.h>
+
+#include "linklist.h"
+#include "stream.h"
+#include "log.h"
+#include "memory.h"
+#include "vty.h"
+#include "if.h"
+#include "prefix.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_debug.h"
+#include "ripngd/ripng_nexthop.h"
+
+DEFINE_MTYPE_STATIC(RIPNGD, RIPNG_RTE_DATA, "RIPng rte data");
+
+#define DEBUG 1
+
+struct ripng_rte_data {
+ struct prefix_ipv6 *p;
+ struct ripng_info *rinfo;
+ struct ripng_aggregate *aggregate;
+};
+
+void _ripng_rte_del(struct ripng_rte_data *A);
+int _ripng_rte_cmp(struct ripng_rte_data *A, struct ripng_rte_data *B);
+
+#define METRIC_OUT(a) \
+ ((a)->rinfo ? (a)->rinfo->metric_out : (a)->aggregate->metric_out)
+#define NEXTHOP_OUT_PTR(a) \
+ ((a)->rinfo ? &((a)->rinfo->nexthop_out) \
+ : &((a)->aggregate->nexthop_out))
+#define TAG_OUT(a) ((a)->rinfo ? (a)->rinfo->tag_out : (a)->aggregate->tag_out)
+
+struct list *ripng_rte_new(void)
+{
+ struct list *rte;
+
+ rte = list_new();
+ rte->cmp = (int (*)(void *, void *))_ripng_rte_cmp;
+ rte->del = (void (*)(void *))_ripng_rte_del;
+
+ return rte;
+}
+
+void ripng_rte_free(struct list *ripng_rte_list)
+{
+ list_delete(&ripng_rte_list);
+}
+
+/* Delete RTE */
+void _ripng_rte_del(struct ripng_rte_data *A)
+{
+ XFREE(MTYPE_RIPNG_RTE_DATA, A);
+}
+
+/* Compare RTE:
+ * return + if A > B
+ * 0 if A = B
+ * - if A < B
+ */
+int _ripng_rte_cmp(struct ripng_rte_data *A, struct ripng_rte_data *B)
+{
+ return addr6_cmp(NEXTHOP_OUT_PTR(A), NEXTHOP_OUT_PTR(B));
+}
+
+/* Add routing table entry */
+void ripng_rte_add(struct list *ripng_rte_list, struct prefix_ipv6 *p,
+ struct ripng_info *rinfo, struct ripng_aggregate *aggregate)
+{
+
+ struct ripng_rte_data *data;
+
+ /* At least one should not be null */
+ assert(!rinfo || !aggregate);
+
+ data = XMALLOC(MTYPE_RIPNG_RTE_DATA, sizeof(*data));
+ data->p = p;
+ data->rinfo = rinfo;
+ data->aggregate = aggregate;
+
+ listnode_add_sort(ripng_rte_list, data);
+}
+
+/* Send the RTE with the nexthop support
+ */
+void ripng_rte_send(struct list *ripng_rte_list, struct interface *ifp,
+ struct sockaddr_in6 *to)
+{
+ struct ripng_interface *ri = ifp->info;
+ struct ripng *ripng = ri->ripng;
+ struct ripng_rte_data *data;
+ struct listnode *node, *nnode;
+
+ struct in6_addr last_nexthop;
+ struct in6_addr myself_nexthop;
+
+ struct stream *s;
+ int num;
+ int mtu;
+ int rtemax;
+ int ret;
+
+ /* Most of the time, there is no nexthop */
+ memset(&last_nexthop, 0, sizeof(last_nexthop));
+
+ /* Use myself_nexthop if the nexthop is not a link-local address,
+ * because
+ * we remain a right path without beeing the optimal one.
+ */
+ memset(&myself_nexthop, 0, sizeof(myself_nexthop));
+
+ /* Output stream get from ripng structre. XXX this should be
+ interface structure. */
+ s = ripng->obuf;
+
+ /* Reset stream and RTE counter. */
+ stream_reset(s);
+ num = 0;
+
+ mtu = ifp->mtu6;
+ if (mtu < 0)
+ mtu = IFMINMTU;
+
+ rtemax = (MIN(mtu, RIPNG_MAX_PACKET_SIZE) - IPV6_HDRLEN
+ - sizeof(struct udphdr) - sizeof(struct ripng_packet)
+ + sizeof(struct rte))
+ / sizeof(struct rte);
+
+ for (ALL_LIST_ELEMENTS(ripng_rte_list, node, nnode, data)) {
+ /* (2.1) Next hop support */
+ if (!IPV6_ADDR_SAME(&last_nexthop, NEXTHOP_OUT_PTR(data))) {
+
+ /* A nexthop entry should be at least followed by 1 RTE
+ */
+ if (num == (rtemax - 1)) {
+ ret = ripng_send_packet((caddr_t)STREAM_DATA(s),
+ stream_get_endp(s), to,
+ ifp);
+
+ if (ret >= 0 && IS_RIPNG_DEBUG_SEND)
+ ripng_packet_dump(
+ (struct ripng_packet *)
+ STREAM_DATA(s),
+ stream_get_endp(s), "SEND");
+ num = 0;
+ stream_reset(s);
+ }
+
+ /* Add the nexthop (2.1) */
+
+ /* If the received next hop address is not a link-local
+ * address,
+ * it should be treated as 0:0:0:0:0:0:0:0.
+ */
+ if (!IN6_IS_ADDR_LINKLOCAL(NEXTHOP_OUT_PTR(data)))
+ last_nexthop = myself_nexthop;
+ else
+ last_nexthop = *NEXTHOP_OUT_PTR(data);
+
+ num = ripng_write_rte(num, s, NULL, &last_nexthop, 0,
+ RIPNG_METRIC_NEXTHOP);
+ } else {
+ /* Rewrite the nexthop for each new packet */
+ if ((num == 0)
+ && !IPV6_ADDR_SAME(&last_nexthop, &myself_nexthop))
+ num = ripng_write_rte(num, s, NULL,
+ &last_nexthop, 0,
+ RIPNG_METRIC_NEXTHOP);
+ }
+ num = ripng_write_rte(num, s, data->p, NULL, TAG_OUT(data),
+ METRIC_OUT(data));
+
+ if (num == rtemax) {
+ ret = ripng_send_packet((caddr_t)STREAM_DATA(s),
+ stream_get_endp(s), to, ifp);
+
+ if (ret >= 0 && IS_RIPNG_DEBUG_SEND)
+ ripng_packet_dump(
+ (struct ripng_packet *)STREAM_DATA(s),
+ stream_get_endp(s), "SEND");
+ num = 0;
+ stream_reset(s);
+ }
+ }
+
+ /* If unwritten RTE exist, flush it. */
+ if (num != 0) {
+ ret = ripng_send_packet((caddr_t)STREAM_DATA(s),
+ stream_get_endp(s), to, ifp);
+
+ if (ret >= 0 && IS_RIPNG_DEBUG_SEND)
+ ripng_packet_dump((struct ripng_packet *)STREAM_DATA(s),
+ stream_get_endp(s), "SEND");
+ stream_reset(s);
+ }
+}
diff --git a/ripngd/ripng_nexthop.h b/ripngd/ripng_nexthop.h
new file mode 100644
index 0000000..2d29939
--- /dev/null
+++ b/ripngd/ripng_nexthop.h
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RIPng nexthop support
+ * Copyright (C) 6WIND Vincent Jardin <vincent.jardin@6wind.com>
+ */
+
+#ifndef _ZEBRA_RIPNG_RIPNG_NEXTHOP_H
+#define _ZEBRA_RIPNG_RIPNG_NEXTHOP_H
+
+#include <zebra.h>
+#include "linklist.h"
+#include "ripngd/ripng_route.h"
+#include "ripngd/ripngd.h"
+
+extern struct list *ripng_rte_new(void);
+extern void ripng_rte_free(struct list *ripng_rte_list);
+extern void ripng_rte_add(struct list *ripng_rte_list, struct prefix_ipv6 *p,
+ struct ripng_info *rinfo,
+ struct ripng_aggregate *aggregate);
+extern void ripng_rte_send(struct list *ripng_rte_list, struct interface *ifp,
+ struct sockaddr_in6 *to);
+
+/***
+ * 1 if A > B
+ * 0 if A = B
+ * -1 if A < B
+ **/
+static inline int addr6_cmp(struct in6_addr *A, struct in6_addr *B)
+{
+#define a(i) A->s6_addr32[i]
+#define b(i) B->s6_addr32[i]
+
+ if (a(3) > b(3))
+ return 1;
+ else if ((a(3) == b(3)) && (a(2) > b(2)))
+ return 1;
+ else if ((a(3) == b(3)) && (a(2) == b(2)) && (a(1) > b(1)))
+ return 1;
+ else if ((a(3) == b(3)) && (a(2) == b(2)) && (a(1) == b(1))
+ && (a(0) > b(0)))
+ return 1;
+
+ if ((a(3) == b(3)) && (a(2) == b(2)) && (a(1) == b(1))
+ && (a(0) == b(0)))
+ return 0;
+
+ return -1;
+}
+
+#endif /* _ZEBRA_RIPNG_RIPNG_NEXTHOP_H */
diff --git a/ripngd/ripng_offset.c b/ripngd/ripng_offset.c
new file mode 100644
index 0000000..d842f15
--- /dev/null
+++ b/ripngd/ripng_offset.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RIPng offset-list
+ * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+ */
+
+/* RIPng support by Vincent Jardin <vincent.jardin@6wind.com>
+ * Copyright (C) 2002 6WIND
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "prefix.h"
+#include "filter.h"
+#include "command.h"
+#include "linklist.h"
+#include "memory.h"
+
+#include "ripngd/ripngd.h"
+
+DEFINE_MTYPE_STATIC(RIPNGD, RIPNG_OFFSET_LIST, "RIPng offset lst");
+
+#define OFFSET_LIST_IN_NAME(O) ((O)->direct[RIPNG_OFFSET_LIST_IN].alist_name)
+#define OFFSET_LIST_IN_METRIC(O) ((O)->direct[RIPNG_OFFSET_LIST_IN].metric)
+
+#define OFFSET_LIST_OUT_NAME(O) ((O)->direct[RIPNG_OFFSET_LIST_OUT].alist_name)
+#define OFFSET_LIST_OUT_METRIC(O) ((O)->direct[RIPNG_OFFSET_LIST_OUT].metric)
+
+struct ripng_offset_list *ripng_offset_list_new(struct ripng *ripng,
+ const char *ifname)
+{
+ struct ripng_offset_list *new;
+
+ new = XCALLOC(MTYPE_RIPNG_OFFSET_LIST,
+ sizeof(struct ripng_offset_list));
+ new->ripng = ripng;
+ new->ifname = strdup(ifname);
+ listnode_add_sort(ripng->offset_list_master, new);
+
+ return new;
+}
+
+void ripng_offset_list_del(struct ripng_offset_list *offset)
+{
+ listnode_delete(offset->ripng->offset_list_master, offset);
+ ripng_offset_list_free(offset);
+}
+
+void ripng_offset_list_free(struct ripng_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_RIPNG_OFFSET_LIST, offset);
+}
+
+struct ripng_offset_list *ripng_offset_list_lookup(struct ripng *ripng,
+ const char *ifname)
+{
+ struct ripng_offset_list *offset;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(ripng->offset_list_master, node, nnode,
+ offset)) {
+ if (strcmp(offset->ifname, ifname) == 0)
+ return offset;
+ }
+ return NULL;
+}
+
+/* If metric is modified return 1. */
+int ripng_offset_list_apply_in(struct ripng *ripng, struct prefix_ipv6 *p,
+ struct interface *ifp, uint8_t *metric)
+{
+ struct ripng_offset_list *offset;
+ struct access_list *alist;
+
+ /* Look up offset-list with interface name. */
+ offset = ripng_offset_list_lookup(ripng, ifp->name);
+ if (offset && OFFSET_LIST_IN_NAME(offset)) {
+ alist = access_list_lookup(AFI_IP6,
+ 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 = ripng_offset_list_lookup(ripng, "*");
+ if (offset && OFFSET_LIST_IN_NAME(offset)) {
+ alist = access_list_lookup(AFI_IP6,
+ 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 ripng_offset_list_apply_out(struct ripng *ripng, struct prefix_ipv6 *p,
+ struct interface *ifp, uint8_t *metric)
+{
+ struct ripng_offset_list *offset;
+ struct access_list *alist;
+
+ /* Look up offset-list with interface name. */
+ offset = ripng_offset_list_lookup(ripng, ifp->name);
+ if (offset && OFFSET_LIST_OUT_NAME(offset)) {
+ alist = access_list_lookup(AFI_IP6,
+ 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 = ripng_offset_list_lookup(ripng, "*");
+ if (offset && OFFSET_LIST_OUT_NAME(offset)) {
+ alist = access_list_lookup(AFI_IP6,
+ 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 ripng_offset_list *o1, struct ripng_offset_list *o2)
+{
+ return strcmp(o1->ifname, o2->ifname);
+}
diff --git a/ripngd/ripng_peer.c b/ripngd/ripng_peer.c
new file mode 100644
index 0000000..901b548
--- /dev/null
+++ b/ripngd/ripng_peer.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RIPng peer support
+ * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+ */
+
+/* RIPng support added by Vincent Jardin <vincent.jardin@6wind.com>
+ * Copyright (C) 2002 6WIND
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "prefix.h"
+#include "command.h"
+#include "linklist.h"
+#include "frrevent.h"
+#include "memory.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_nexthop.h"
+
+DEFINE_MTYPE_STATIC(RIPNGD, RIPNG_PEER, "RIPng peer");
+
+static struct ripng_peer *ripng_peer_new(void)
+{
+ return XCALLOC(MTYPE_RIPNG_PEER, sizeof(struct ripng_peer));
+}
+
+static void ripng_peer_free(struct ripng_peer *peer)
+{
+ EVENT_OFF(peer->t_timeout);
+ XFREE(MTYPE_RIPNG_PEER, peer);
+}
+
+struct ripng_peer *ripng_peer_lookup(struct ripng *ripng, struct in6_addr *addr)
+{
+ struct ripng_peer *peer;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(ripng->peer_list, node, nnode, peer)) {
+ if (IPV6_ADDR_SAME(&peer->addr, addr))
+ return peer;
+ }
+ return NULL;
+}
+
+struct ripng_peer *ripng_peer_lookup_next(struct ripng *ripng,
+ struct in6_addr *addr)
+{
+ struct ripng_peer *peer;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(ripng->peer_list, node, nnode, peer)) {
+ if (addr6_cmp(&peer->addr, addr) > 0)
+ return peer;
+ }
+ return NULL;
+}
+
+/* RIPng peer is timeout.
+ * Garbage collector.
+ **/
+static void ripng_peer_timeout(struct event *t)
+{
+ struct ripng_peer *peer;
+
+ peer = EVENT_ARG(t);
+ listnode_delete(peer->ripng->peer_list, peer);
+ ripng_peer_free(peer);
+}
+
+/* Get RIPng peer. At the same time update timeout thread. */
+static struct ripng_peer *ripng_peer_get(struct ripng *ripng,
+ struct in6_addr *addr)
+{
+ struct ripng_peer *peer;
+
+ peer = ripng_peer_lookup(ripng, addr);
+
+ if (peer) {
+ EVENT_OFF(peer->t_timeout);
+ } else {
+ peer = ripng_peer_new();
+ peer->ripng = ripng;
+ peer->addr = *addr;
+ listnode_add_sort(ripng->peer_list, peer);
+ }
+
+ /* Update timeout thread. */
+ event_add_timer(master, ripng_peer_timeout, peer,
+ RIPNG_PEER_TIMER_DEFAULT, &peer->t_timeout);
+
+ /* Last update time set. */
+ time(&peer->uptime);
+
+ return peer;
+}
+
+void ripng_peer_update(struct ripng *ripng, struct sockaddr_in6 *from,
+ uint8_t version)
+{
+ struct ripng_peer *peer;
+ peer = ripng_peer_get(ripng, &from->sin6_addr);
+ peer->version = version;
+}
+
+void ripng_peer_bad_route(struct ripng *ripng, struct sockaddr_in6 *from)
+{
+ struct ripng_peer *peer;
+ peer = ripng_peer_get(ripng, &from->sin6_addr);
+ peer->recv_badroutes++;
+}
+
+void ripng_peer_bad_packet(struct ripng *ripng, struct sockaddr_in6 *from)
+{
+ struct ripng_peer *peer;
+ peer = ripng_peer_get(ripng, &from->sin6_addr);
+ peer->recv_badpackets++;
+}
+
+/* Display peer uptime. */
+static char *ripng_peer_uptime(struct ripng_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 ripng_peer_display(struct vty *vty, struct ripng *ripng)
+{
+ struct ripng_peer *peer;
+ struct listnode *node, *nnode;
+#define RIPNG_UPTIME_LEN 25
+ char timebuf[RIPNG_UPTIME_LEN];
+
+ for (ALL_LIST_ELEMENTS(ripng->peer_list, node, nnode, peer)) {
+ vty_out(vty, " %pI6 \n%14s %10d %10d %10d %s\n",
+ &peer->addr, " ", peer->recv_badpackets,
+ peer->recv_badroutes, ZEBRA_RIPNG_DISTANCE_DEFAULT,
+ ripng_peer_uptime(peer, timebuf, RIPNG_UPTIME_LEN));
+ }
+}
+
+int ripng_peer_list_cmp(struct ripng_peer *p1, struct ripng_peer *p2)
+{
+ return memcmp(&p1->addr, &p2->addr, sizeof(struct in6_addr));
+}
+
+void ripng_peer_list_del(void *arg)
+{
+ ripng_peer_free(arg);
+}
diff --git a/ripngd/ripng_route.c b/ripngd/ripng_route.c
new file mode 100644
index 0000000..80041fb
--- /dev/null
+++ b/ripngd/ripng_route.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RIPng routes function.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "agg_table.h"
+#include "memory.h"
+#include "if.h"
+#include "vty.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_route.h"
+
+DEFINE_MTYPE_STATIC(RIPNGD, RIPNG_AGGREGATE, "RIPng aggregate");
+
+static struct ripng_aggregate *ripng_aggregate_new(void)
+{
+ struct ripng_aggregate *new;
+
+ new = XCALLOC(MTYPE_RIPNG_AGGREGATE, sizeof(struct ripng_aggregate));
+ return new;
+}
+
+void ripng_aggregate_free(struct ripng_aggregate *aggregate)
+{
+ XFREE(MTYPE_RIPNG_AGGREGATE, aggregate);
+}
+
+/* Aggregate count increment check. */
+void ripng_aggregate_increment(struct agg_node *child, struct ripng_info *rinfo)
+{
+ struct agg_node *np;
+ struct ripng_aggregate *aggregate;
+
+ for (np = child; np; np = agg_node_parent(np))
+ if ((aggregate = np->aggregate) != NULL) {
+ aggregate->count++;
+ rinfo->suppress++;
+ }
+}
+
+/* Aggregate count decrement check. */
+void ripng_aggregate_decrement(struct agg_node *child, struct ripng_info *rinfo)
+{
+ struct agg_node *np;
+ struct ripng_aggregate *aggregate;
+
+ for (np = child; np; np = agg_node_parent(np))
+ if ((aggregate = np->aggregate) != NULL) {
+ aggregate->count--;
+ rinfo->suppress--;
+ }
+}
+
+/* Aggregate count decrement check for a list. */
+void ripng_aggregate_decrement_list(struct agg_node *child, struct list *list)
+{
+ struct agg_node *np;
+ struct ripng_aggregate *aggregate;
+ struct ripng_info *rinfo = NULL;
+ struct listnode *node = NULL;
+
+ for (np = child; np; np = agg_node_parent(np))
+ if ((aggregate = np->aggregate) != NULL)
+ aggregate->count -= listcount(list);
+
+ for (ALL_LIST_ELEMENTS_RO(list, node, rinfo))
+ rinfo->suppress--;
+}
+
+/* RIPng routes treatment. */
+int ripng_aggregate_add(struct ripng *ripng, struct prefix *p)
+{
+ struct agg_node *top;
+ struct agg_node *rp;
+ struct ripng_info *rinfo;
+ struct ripng_aggregate *aggregate;
+ struct ripng_aggregate *sub;
+ struct list *list = NULL;
+ struct listnode *node = NULL;
+
+ /* Get top node for aggregation. */
+ top = agg_node_get(ripng->table, p);
+
+ /* Allocate new aggregate. */
+ aggregate = ripng_aggregate_new();
+ aggregate->metric = 1;
+
+ top->aggregate = aggregate;
+
+ /* Suppress routes match to the aggregate. */
+ for (rp = agg_lock_node(top); rp; rp = agg_route_next_until(rp, top)) {
+ /* Suppress normal route. */
+ if ((list = rp->info) != NULL)
+ for (ALL_LIST_ELEMENTS_RO(list, node, rinfo)) {
+ aggregate->count++;
+ rinfo->suppress++;
+ }
+ /* Suppress aggregate route. This may not need. */
+ if (rp != top && (sub = rp->aggregate) != NULL) {
+ aggregate->count++;
+ sub->suppress++;
+ }
+ }
+
+ return 0;
+}
+
+/* Delete RIPng static route. */
+int ripng_aggregate_delete(struct ripng *ripng, struct prefix *p)
+{
+ struct agg_node *top;
+ struct agg_node *rp;
+ struct ripng_info *rinfo;
+ struct ripng_aggregate *aggregate;
+ struct ripng_aggregate *sub;
+ struct list *list = NULL;
+ struct listnode *node = NULL;
+
+ /* Get top node for aggregation. */
+ top = agg_node_get(ripng->table, p);
+
+ /* Allocate new aggregate. */
+ aggregate = top->aggregate;
+
+ /* Suppress routes match to the aggregate. */
+ for (rp = agg_lock_node(top); rp; rp = agg_route_next_until(rp, top)) {
+ /* Suppress normal route. */
+ if ((list = rp->info) != NULL)
+ for (ALL_LIST_ELEMENTS_RO(list, node, rinfo)) {
+ aggregate->count--;
+ rinfo->suppress--;
+ }
+
+ if (rp != top && (sub = rp->aggregate) != NULL) {
+ aggregate->count--;
+ sub->suppress--;
+ }
+ }
+
+ top->aggregate = NULL;
+ ripng_aggregate_free(aggregate);
+
+ agg_unlock_node(top);
+ agg_unlock_node(top);
+
+ return 0;
+}
diff --git a/ripngd/ripng_route.h b/ripngd/ripng_route.h
new file mode 100644
index 0000000..fb3d71c
--- /dev/null
+++ b/ripngd/ripng_route.h
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RIPng daemon
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ */
+
+#ifndef _ZEBRA_RIPNG_ROUTE_H
+#define _ZEBRA_RIPNG_ROUTE_H
+
+struct ripng_aggregate {
+ /* Aggregate route count. */
+ unsigned int count;
+
+ /* Suppressed route count. */
+ unsigned int suppress;
+
+ /* Metric of this route. */
+ uint8_t metric;
+
+ /* Tag field of RIPng packet.*/
+ uint16_t tag;
+
+ /* Route-map futures - this variables can be changed. */
+ struct in6_addr nexthop_out;
+ uint8_t metric_set;
+ uint8_t metric_out;
+ uint16_t tag_out;
+};
+
+extern void ripng_aggregate_increment(struct agg_node *rp,
+ struct ripng_info *rinfo);
+extern void ripng_aggregate_decrement(struct agg_node *rp,
+ struct ripng_info *rinfo);
+extern void ripng_aggregate_decrement_list(struct agg_node *rp,
+ struct list *list);
+extern int ripng_aggregate_add(struct ripng *ripng, struct prefix *p);
+extern int ripng_aggregate_delete(struct ripng *ripng, struct prefix *p);
+extern void ripng_aggregate_free(struct ripng_aggregate *aggregate);
+
+#endif /* _ZEBRA_RIPNG_ROUTE_H */
diff --git a/ripngd/ripng_routemap.c b/ripngd/ripng_routemap.c
new file mode 100644
index 0000000..b5f74be
--- /dev/null
+++ b/ripngd/ripng_routemap.c
@@ -0,0 +1,423 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RIPng routemap.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "memory.h"
+#include "prefix.h"
+#include "vty.h"
+#include "routemap.h"
+#include "command.h"
+#include "sockunion.h"
+
+#include "ripngd/ripngd.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;
+ struct ripng_info *rinfo;
+
+ metric = rule;
+ rinfo = object;
+
+ if (rinfo->metric == *metric)
+ return RMAP_MATCH;
+ else
+ return RMAP_NOMATCH;
+
+ 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 ripng_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 == ifp->ifindex)
+ return RMAP_MATCH;
+ else
+ return RMAP_NOMATCH;
+
+ return RMAP_NOMATCH;
+}
+
+/* Route map `match interface' match statement. `arg' is IFNAME value */
+static void *route_match_interface_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void route_match_interface_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd route_match_interface_cmd = {
+ "interface",
+ route_match_interface,
+ route_match_interface_compile,
+ route_match_interface_free
+};
+
+/* match ipv6 address WORD */
+
+static enum route_map_cmd_result_t
+route_match_ipv6_address(void *rule, const struct prefix *prefix, void *object)
+{
+ struct access_list *alist;
+
+ alist = access_list_lookup(AFI_IP6, (char *)rule);
+ if (access_list_apply(alist, prefix) != FILTER_DENY)
+ return RMAP_MATCH;
+
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_ipv6_address_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void route_match_ipv6_address_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd route_match_ipv6_address_cmd = {
+ "ipv6 address",
+ route_match_ipv6_address,
+ route_match_ipv6_address_compile,
+ route_match_ipv6_address_free
+};
+
+/* match ipv6 address prefix-list PREFIX_LIST */
+
+static enum route_map_cmd_result_t
+route_match_ipv6_address_prefix_list(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ struct prefix_list *plist;
+
+ plist = prefix_list_lookup(AFI_IP6, (char *)rule);
+ if (prefix_list_apply(plist, prefix) != PREFIX_DENY)
+ return RMAP_MATCH;
+
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_ipv6_address_prefix_list_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void route_match_ipv6_address_prefix_list_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd
+ route_match_ipv6_address_prefix_list_cmd = {
+ "ipv6 address prefix-list",
+ route_match_ipv6_address_prefix_list,
+ route_match_ipv6_address_prefix_list_compile,
+ route_match_ipv6_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 *prefix,
+ void *object)
+{
+ route_tag_t *tag;
+ struct ripng_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;
+}
+
+
+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 ripng_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 (rinfo->metric_out < 1)
+ rinfo->metric_out = 1;
+ if (rinfo->metric_out > RIPNG_METRIC_INFINITY)
+ rinfo->metric_out = RIPNG_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;
+
+ len = strlen(arg);
+ pnt = arg;
+
+ mod = XMALLOC(MTYPE_ROUTE_MAP_COMPILED,
+ sizeof(struct rip_metric_modifier));
+ mod->used = false;
+
+ 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 > RIPNG_METRIC_INFINITY) {
+ zlog_info(
+ "%s: Metric specified: %ld is being converted into METRIC_INFINITY",
+ __func__, metric);
+ mod->metric = RIPNG_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);
+}
+
+static const struct route_map_rule_cmd route_set_metric_cmd = {
+ "metric",
+ route_set_metric,
+ route_set_metric_compile,
+ route_set_metric_free,
+};
+
+/* `set ipv6 next-hop local IP_ADDRESS' */
+
+/* Set nexthop to object. object must be pointer to struct attr. */
+static enum route_map_cmd_result_t
+route_set_ipv6_nexthop_local(void *rule, const struct prefix *p, void *object)
+{
+ struct in6_addr *address;
+ struct ripng_info *rinfo;
+
+ /* Fetch routemap's rule information. */
+ address = rule;
+ rinfo = object;
+
+ /* Set next hop value. */
+ rinfo->nexthop_out = *address;
+
+ return RMAP_OKAY;
+}
+
+/* Route map `ipv6 nexthop local' compile function. Given string is converted
+ to struct in6_addr structure. */
+static void *route_set_ipv6_nexthop_local_compile(const char *arg)
+{
+ int ret;
+ struct in6_addr *address;
+
+ address = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct in6_addr));
+
+ ret = inet_pton(AF_INET6, arg, address);
+
+ if (ret == 0) {
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, address);
+ return NULL;
+ }
+
+ return address;
+}
+
+/* Free route map's compiled `ipv6 nexthop local' value. */
+static void route_set_ipv6_nexthop_local_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ipv6 nexthop local set. */
+static const struct route_map_rule_cmd
+ route_set_ipv6_nexthop_local_cmd = {
+ "ipv6 next-hop local",
+ route_set_ipv6_nexthop_local,
+ route_set_ipv6_nexthop_local_compile,
+ route_set_ipv6_nexthop_local_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 ripng_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"
+
+void ripng_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_ipv6_address_hook(generic_match_add);
+ route_map_no_match_ipv6_address_hook(generic_match_delete);
+
+ route_map_match_ipv6_address_prefix_list_hook(generic_match_add);
+ route_map_no_match_ipv6_address_prefix_list_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_ipv6_nexthop_local_hook(generic_set_add);
+ route_map_no_set_ipv6_nexthop_local_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_ipv6_address_cmd);
+ route_map_install_match(&route_match_ipv6_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_ipv6_nexthop_local_cmd);
+ route_map_install_set(&route_set_tag_cmd);
+}
diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c
new file mode 100644
index 0000000..bb5a880
--- /dev/null
+++ b/ripngd/ripng_zebra.c
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RIPngd and zebra interface.
+ * Copyright (C) 1998, 1999 Kunihiro Ishiguro
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "prefix.h"
+#include "agg_table.h"
+#include "stream.h"
+#include "memory.h"
+#include "routemap.h"
+#include "zclient.h"
+#include "log.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_debug.h"
+
+/* All information about zebra. */
+struct zclient *zclient = NULL;
+
+/* Send ECMP routes to zebra. */
+static void ripng_zebra_ipv6_send(struct ripng *ripng, struct agg_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 ripng_info *rinfo = NULL;
+ uint32_t count = 0;
+ const struct prefix *p = agg_node_get_prefix(rp);
+
+ memset(&api, 0, sizeof(api));
+ api.vrf_id = ripng->vrf->vrf_id;
+ api.type = ZEBRA_ROUTE_RIPNG;
+ api.safi = SAFI_UNICAST;
+ api.prefix = *p;
+
+ 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 = ripng->vrf->vrf_id;
+ api_nh->gate.ipv6 = rinfo->nexthop;
+ api_nh->ifindex = rinfo->ifindex;
+ api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ count++;
+ if (cmd == ZEBRA_ROUTE_ADD)
+ SET_FLAG(rinfo->flags, RIPNG_RTF_FIB);
+ else
+ UNSET_FLAG(rinfo->flags, RIPNG_RTF_FIB);
+ }
+
+ api.nexthop_num = count;
+
+ rinfo = listgetdata(listhead(list));
+
+ SET_FLAG(api.message, ZAPI_MESSAGE_METRIC);
+ api.metric = rinfo->metric;
+
+ if (rinfo->tag) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_TAG);
+ api.tag = rinfo->tag;
+ }
+
+ zclient_route_send(cmd, zclient, &api);
+
+ if (IS_RIPNG_DEBUG_ZEBRA) {
+ if (ripng->ecmp)
+ zlog_debug("%s: %pRN nexthops %d",
+ (cmd == ZEBRA_ROUTE_ADD)
+ ? "Install into zebra"
+ : "Delete from zebra",
+ rp, count);
+ else
+ zlog_debug("%s: %pRN",
+ (cmd == ZEBRA_ROUTE_ADD)
+ ? "Install into zebra"
+ : "Delete from zebra",
+ rp);
+ }
+}
+
+/* Add/update ECMP routes to zebra. */
+void ripng_zebra_ipv6_add(struct ripng *ripng, struct agg_node *rp)
+{
+ ripng_zebra_ipv6_send(ripng, rp, ZEBRA_ROUTE_ADD);
+}
+
+/* Delete ECMP routes from zebra. */
+void ripng_zebra_ipv6_delete(struct ripng *ripng, struct agg_node *rp)
+{
+ ripng_zebra_ipv6_send(ripng, rp, ZEBRA_ROUTE_DELETE);
+}
+
+/* Zebra route add and delete treatment. */
+static int ripng_zebra_read_route(ZAPI_CALLBACK_ARGS)
+{
+ struct ripng *ripng;
+ struct zapi_route api;
+ struct in6_addr nexthop;
+ unsigned long ifindex;
+
+ ripng = ripng_lookup_by_vrf_id(vrf_id);
+ if (!ripng)
+ return 0;
+
+ if (zapi_route_decode(zclient->ibuf, &api) < 0)
+ return -1;
+
+ /* we completely ignore srcdest routes for now. */
+ if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX))
+ return 0;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&api.prefix.u.prefix6))
+ return 0;
+
+ nexthop = api.nexthops[0].gate.ipv6;
+ ifindex = api.nexthops[0].ifindex;
+
+ if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
+ ripng_redistribute_add(ripng, api.type,
+ RIPNG_ROUTE_REDISTRIBUTE,
+ (struct prefix_ipv6 *)&api.prefix,
+ ifindex, &nexthop, api.tag);
+ else
+ ripng_redistribute_delete(
+ ripng, api.type, RIPNG_ROUTE_REDISTRIBUTE,
+ (struct prefix_ipv6 *)&api.prefix, ifindex);
+
+ return 0;
+}
+
+void ripng_redistribute_conf_update(struct ripng *ripng, int type)
+{
+ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6,
+ type, 0, ripng->vrf->vrf_id);
+}
+
+void ripng_redistribute_conf_delete(struct ripng *ripng, int type)
+{
+ if (zclient->sock > 0)
+ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_DELETE, zclient,
+ AFI_IP6, type, 0, ripng->vrf->vrf_id);
+
+ ripng_redistribute_withdraw(ripng, type);
+}
+
+int ripng_redistribute_check(struct ripng *ripng, int type)
+{
+ return ripng->redist[type].enabled;
+}
+
+void ripng_redistribute_enable(struct ripng *ripng)
+{
+ for (int i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+ if (!ripng_redistribute_check(ripng, i))
+ continue;
+
+ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient,
+ AFI_IP6, i, 0, ripng->vrf->vrf_id);
+ }
+}
+
+void ripng_redistribute_disable(struct ripng *ripng)
+{
+ for (int i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+ if (!ripng_redistribute_check(ripng, i))
+ continue;
+
+ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_DELETE, zclient,
+ AFI_IP6, i, 0, ripng->vrf->vrf_id);
+ }
+}
+
+void ripng_redistribute_write(struct vty *vty, struct ripng *ripng)
+{
+ int i;
+
+ for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+ if (i == zclient->redist_default
+ || !ripng_redistribute_check(ripng, i))
+ continue;
+
+ vty_out(vty, " %s", zebra_route_string(i));
+ }
+}
+
+void ripng_zebra_vrf_register(struct vrf *vrf)
+{
+ if (vrf->vrf_id == VRF_DEFAULT)
+ return;
+
+ if (IS_RIPNG_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);
+}
+
+void ripng_zebra_vrf_deregister(struct vrf *vrf)
+{
+ if (vrf->vrf_id == VRF_DEFAULT)
+ return;
+
+ if (IS_RIPNG_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);
+}
+
+static void ripng_zebra_connected(struct zclient *zclient)
+{
+ zclient_send_reg_requests(zclient, VRF_DEFAULT);
+}
+
+static zclient_handler *const ripng_handlers[] = {
+ [ZEBRA_INTERFACE_ADDRESS_ADD] = ripng_interface_address_add,
+ [ZEBRA_INTERFACE_ADDRESS_DELETE] = ripng_interface_address_delete,
+ [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = ripng_zebra_read_route,
+ [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = ripng_zebra_read_route,
+};
+
+static void ripng_zebra_capabilities(struct zclient_capabilities *cap)
+{
+ zebra_ecmp_count = MIN(cap->ecmp, zebra_ecmp_count);
+}
+
+/* Initialize zebra structure and it's commands. */
+void zebra_init(struct event_loop *master)
+{
+ /* Allocate zebra structure. */
+ zclient = zclient_new(master, &zclient_options_default, ripng_handlers,
+ array_size(ripng_handlers));
+ zclient_init(zclient, ZEBRA_ROUTE_RIPNG, 0, &ripngd_privs);
+
+ zclient->zebra_connected = ripng_zebra_connected;
+ zclient->zebra_capabilities = ripng_zebra_capabilities;
+}
+
+void ripng_zebra_stop(void)
+{
+ zclient_stop(zclient);
+ zclient_free(zclient);
+}
diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c
new file mode 100644
index 0000000..465b40b
--- /dev/null
+++ b/ripngd/ripngd.c
@@ -0,0 +1,2718 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RIPng daemon
+ * Copyright (C) 1998, 1999 Kunihiro Ishiguro
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "filter.h"
+#include "log.h"
+#include "frrevent.h"
+#include "memory.h"
+#include "if.h"
+#include "stream.h"
+#include "agg_table.h"
+#include "command.h"
+#include "sockopt.h"
+#include "distribute.h"
+#include "plist.h"
+#include "routemap.h"
+#include "if_rmap.h"
+#include "privs.h"
+#include "lib_errors.h"
+#include "northbound_cli.h"
+#include "network.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_route.h"
+#include "ripngd/ripng_debug.h"
+#include "ripngd/ripng_nexthop.h"
+
+DEFINE_MGROUP(RIPNGD, "ripngd");
+DEFINE_MTYPE_STATIC(RIPNGD, RIPNG, "RIPng structure");
+DEFINE_MTYPE_STATIC(RIPNGD, RIPNG_VRF_NAME, "RIPng VRF name");
+DEFINE_MTYPE_STATIC(RIPNGD, RIPNG_ROUTE, "RIPng route info");
+
+enum { ripng_all_route,
+ ripng_changed_route,
+};
+
+static void ripng_distribute_update(struct distribute_ctx *ctx,
+ struct distribute *dist);
+
+/* Prototypes. */
+void ripng_output_process(struct interface *, struct sockaddr_in6 *, int);
+static void ripng_instance_enable(struct ripng *ripng, struct vrf *vrf,
+ int sock);
+static void ripng_instance_disable(struct ripng *ripng);
+static void ripng_triggered_update(struct event *);
+static void ripng_if_rmap_update(struct if_rmap_ctx *ctx,
+ struct if_rmap *if_rmap);
+
+/* Generate rb-tree of RIPng instances. */
+static inline int ripng_instance_compare(const struct ripng *a,
+ const struct ripng *b)
+{
+ return strcmp(a->vrf_name, b->vrf_name);
+}
+RB_GENERATE(ripng_instance_head, ripng, entry, ripng_instance_compare)
+
+struct ripng_instance_head ripng_instances = RB_INITIALIZER(&ripng_instances);
+
+/* RIPng next hop specification. */
+struct ripng_nexthop {
+ enum ripng_nexthop_type {
+ RIPNG_NEXTHOP_UNSPEC,
+ RIPNG_NEXTHOP_ADDRESS
+ } flag;
+ struct in6_addr address;
+};
+
+int ripng_route_rte(struct ripng_info *rinfo)
+{
+ return (rinfo->type == ZEBRA_ROUTE_RIPNG
+ && rinfo->sub_type == RIPNG_ROUTE_RTE);
+}
+
+/* Allocate new ripng information. */
+struct ripng_info *ripng_info_new(void)
+{
+ struct ripng_info *new;
+
+ new = XCALLOC(MTYPE_RIPNG_ROUTE, sizeof(struct ripng_info));
+ return new;
+}
+
+/* Free ripng information. */
+void ripng_info_free(struct ripng_info *rinfo)
+{
+ XFREE(MTYPE_RIPNG_ROUTE, rinfo);
+}
+
+struct ripng *ripng_info_get_instance(const struct ripng_info *rinfo)
+{
+ return agg_get_table_info(agg_get_table(rinfo->rp));
+}
+
+/* Create ripng socket. */
+int ripng_make_socket(struct vrf *vrf)
+{
+ int ret;
+ int sock;
+ struct sockaddr_in6 ripaddr;
+ const char *vrf_dev = NULL;
+
+ /* Make datagram socket. */
+ if (vrf->vrf_id != VRF_DEFAULT)
+ vrf_dev = vrf->name;
+ frr_with_privs(&ripngd_privs) {
+ sock = vrf_socket(AF_INET6, 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_reuseaddr(sock);
+ sockopt_reuseport(sock);
+ setsockopt_so_recvbuf(sock, 8096);
+ ret = setsockopt_ipv6_pktinfo(sock, 1);
+ if (ret < 0)
+ goto error;
+#ifdef IPTOS_PREC_INTERNETCONTROL
+ ret = setsockopt_ipv6_tclass(sock, IPTOS_PREC_INTERNETCONTROL);
+ if (ret < 0)
+ goto error;
+#endif
+ ret = setsockopt_ipv6_multicast_hops(sock, 255);
+ if (ret < 0)
+ goto error;
+ ret = setsockopt_ipv6_multicast_loop(sock, 0);
+ if (ret < 0)
+ goto error;
+ ret = setsockopt_ipv6_hoplimit(sock, 1);
+ if (ret < 0)
+ goto error;
+
+ memset(&ripaddr, 0, sizeof(ripaddr));
+ ripaddr.sin6_family = AF_INET6;
+#ifdef SIN6_LEN
+ ripaddr.sin6_len = sizeof(struct sockaddr_in6);
+#endif /* SIN6_LEN */
+ ripaddr.sin6_port = htons(RIPNG_PORT_DEFAULT);
+
+ frr_with_privs(&ripngd_privs) {
+ ret = bind(sock, (struct sockaddr *)&ripaddr, sizeof(ripaddr));
+ if (ret < 0) {
+ zlog_err("Can't bind ripng socket: %s.",
+ safe_strerror(errno));
+ goto error;
+ }
+ }
+ return sock;
+
+error:
+ close(sock);
+ return ret;
+}
+
+/* Send RIPng packet. */
+int ripng_send_packet(caddr_t buf, int bufsize, struct sockaddr_in6 *to,
+ struct interface *ifp)
+{
+ struct ripng_interface *ri = ifp->info;
+ struct ripng *ripng = ri->ripng;
+ int ret;
+ struct msghdr msg;
+ struct iovec iov;
+ struct cmsghdr *cmsgptr;
+ char adata[256] = {};
+ struct in6_pktinfo *pkt;
+ struct sockaddr_in6 addr;
+
+ if (IS_RIPNG_DEBUG_SEND) {
+ if (to)
+ zlog_debug("send to %pI6", &to->sin6_addr);
+ zlog_debug(" send interface %s", ifp->name);
+ zlog_debug(" send packet size %d", bufsize);
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+#ifdef SIN6_LEN
+ addr.sin6_len = sizeof(struct sockaddr_in6);
+#endif /* SIN6_LEN */
+ addr.sin6_flowinfo = htonl(RIPNG_PRIORITY_DEFAULT);
+
+ /* When destination is specified. */
+ if (to != NULL) {
+ addr.sin6_addr = to->sin6_addr;
+ addr.sin6_port = to->sin6_port;
+ } else {
+ inet_pton(AF_INET6, RIPNG_GROUP, &addr.sin6_addr);
+ addr.sin6_port = htons(RIPNG_PORT_DEFAULT);
+ }
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = (void *)&addr;
+ msg.msg_namelen = sizeof(struct sockaddr_in6);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = adata;
+ msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
+
+ iov.iov_base = buf;
+ iov.iov_len = bufsize;
+
+ cmsgptr = (struct cmsghdr *)adata;
+ cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ cmsgptr->cmsg_level = IPPROTO_IPV6;
+ cmsgptr->cmsg_type = IPV6_PKTINFO;
+
+ pkt = (struct in6_pktinfo *)CMSG_DATA(cmsgptr);
+ memset(&pkt->ipi6_addr, 0, sizeof(struct in6_addr));
+ pkt->ipi6_ifindex = ifp->ifindex;
+
+ ret = sendmsg(ripng->sock, &msg, 0);
+
+ if (ret < 0) {
+ if (to)
+ flog_err_sys(EC_LIB_SOCKET,
+ "RIPng send fail on %s to %pI6: %s",
+ ifp->name, &to->sin6_addr,
+ safe_strerror(errno));
+ else
+ flog_err_sys(EC_LIB_SOCKET, "RIPng send fail on %s: %s",
+ ifp->name, safe_strerror(errno));
+ }
+
+ return ret;
+}
+
+/* Receive UDP RIPng packet from socket. */
+static int ripng_recv_packet(int sock, uint8_t *buf, int bufsize,
+ struct sockaddr_in6 *from, ifindex_t *ifindex,
+ int *hoplimit)
+{
+ int ret;
+ struct msghdr msg;
+ struct iovec iov;
+ struct cmsghdr *cmsgptr;
+ struct in6_addr dst = {.s6_addr = {0}};
+
+ memset(&dst, 0, sizeof(dst));
+
+ /* Ancillary data. This store cmsghdr and in6_pktinfo. But at this
+ point I can't determine size of cmsghdr */
+ char adata[1024];
+
+ /* Fill in message and iovec. */
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = (void *)from;
+ msg.msg_namelen = sizeof(struct sockaddr_in6);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = (void *)adata;
+ msg.msg_controllen = sizeof(adata);
+ iov.iov_base = buf;
+ iov.iov_len = bufsize;
+
+ /* If recvmsg fail return minus value. */
+ ret = recvmsg(sock, &msg, 0);
+ if (ret < 0)
+ return ret;
+
+ for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL;
+ cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
+ /* I want interface index which this packet comes from. */
+ if (cmsgptr->cmsg_level == IPPROTO_IPV6
+ && cmsgptr->cmsg_type == IPV6_PKTINFO) {
+ struct in6_pktinfo *ptr;
+
+ ptr = (struct in6_pktinfo *)CMSG_DATA(cmsgptr);
+ *ifindex = ptr->ipi6_ifindex;
+ dst = ptr->ipi6_addr;
+
+ if (*ifindex == 0)
+ zlog_warn(
+ "Interface index returned by IPV6_PKTINFO is zero");
+ }
+
+ /* Incoming packet's multicast hop limit. */
+ if (cmsgptr->cmsg_level == IPPROTO_IPV6
+ && cmsgptr->cmsg_type == IPV6_HOPLIMIT) {
+ int *phoplimit = (int *)CMSG_DATA(cmsgptr);
+ *hoplimit = *phoplimit;
+ }
+ }
+
+ /* Hoplimit check shold be done when destination address is
+ multicast address. */
+ if (!IN6_IS_ADDR_MULTICAST(&dst))
+ *hoplimit = -1;
+
+ return ret;
+}
+
+/* Dump rip packet */
+void ripng_packet_dump(struct ripng_packet *packet, int size,
+ const char *sndrcv)
+{
+ caddr_t lim;
+ struct rte *rte;
+ const char *command_str;
+
+ /* Set command string. */
+ if (packet->command == RIPNG_REQUEST)
+ command_str = "request";
+ else if (packet->command == RIPNG_RESPONSE)
+ command_str = "response";
+ 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 (rte->metric == RIPNG_METRIC_NEXTHOP)
+ zlog_debug(" nexthop %pI6/%d", &rte->addr,
+ rte->prefixlen);
+ else
+ zlog_debug(" %pI6/%d metric %d tag %" ROUTE_TAG_PRI,
+ &rte->addr, rte->prefixlen,
+ rte->metric, (route_tag_t)ntohs(rte->tag));
+ }
+}
+
+/* RIPng next hop address RTE (Route Table Entry). */
+static void ripng_nexthop_rte(struct rte *rte, struct sockaddr_in6 *from,
+ struct ripng_nexthop *nexthop)
+{
+ /* Logging before checking RTE. */
+ if (IS_RIPNG_DEBUG_RECV)
+ zlog_debug("RIPng nexthop RTE address %pI6 tag %" ROUTE_TAG_PRI
+ " prefixlen %d",
+ &rte->addr, (route_tag_t)ntohs(rte->tag),
+ rte->prefixlen);
+
+ /* RFC2080 2.1.1 Next Hop:
+ The route tag and prefix length in the next hop RTE must be
+ set to zero on sending and ignored on receiption. */
+ if (ntohs(rte->tag) != 0)
+ zlog_warn(
+ "RIPng nexthop RTE with non zero tag value %" ROUTE_TAG_PRI
+ " from %pI6",
+ (route_tag_t)ntohs(rte->tag), &from->sin6_addr);
+
+ if (rte->prefixlen != 0)
+ zlog_warn(
+ "RIPng nexthop RTE with non zero prefixlen value %d from %pI6",
+ rte->prefixlen, &from->sin6_addr);
+
+ /* Specifying a value of 0:0:0:0:0:0:0:0 in the prefix field of a
+ next hop RTE indicates that the next hop address should be the
+ originator of the RIPng advertisement. An address specified as a
+ next hop must be a link-local address. */
+ if (IN6_IS_ADDR_UNSPECIFIED(&rte->addr)) {
+ nexthop->flag = RIPNG_NEXTHOP_UNSPEC;
+ memset(&nexthop->address, 0, sizeof(struct in6_addr));
+ return;
+ }
+
+ if (IN6_IS_ADDR_LINKLOCAL(&rte->addr)) {
+ nexthop->flag = RIPNG_NEXTHOP_ADDRESS;
+ IPV6_ADDR_COPY(&nexthop->address, &rte->addr);
+ return;
+ }
+
+ /* The purpose of the next hop RTE is to eliminate packets being
+ routed through extra hops in the system. It is particularly useful
+ when RIPng is not being run on all of the routers on a network.
+ Note that next hop RTE is "advisory". That is, if the provided
+ information is ignored, a possibly sub-optimal, but absolutely
+ valid, route may be taken. If the received next hop address is not
+ a link-local address, it should be treated as 0:0:0:0:0:0:0:0. */
+ zlog_warn("RIPng nexthop RTE with non link-local address %pI6 from %pI6",
+ &rte->addr, &from->sin6_addr);
+
+ nexthop->flag = RIPNG_NEXTHOP_UNSPEC;
+ memset(&nexthop->address, 0, sizeof(struct in6_addr));
+
+ return;
+}
+
+/* If ifp has same link-local address then return 1. */
+static int ripng_lladdr_check(struct interface *ifp, struct in6_addr *addr)
+{
+ struct listnode *node;
+ struct connected *connected;
+ struct prefix *p;
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) {
+ p = connected->address;
+
+ if (p->family == AF_INET6
+ && IN6_IS_ADDR_LINKLOCAL(&p->u.prefix6)
+ && IN6_ARE_ADDR_EQUAL(&p->u.prefix6, addr))
+ return 1;
+ }
+ return 0;
+}
+
+/* RIPng route garbage collect timer. */
+static void ripng_garbage_collect(struct event *t)
+{
+ struct ripng_info *rinfo;
+ struct agg_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);
+ agg_unlock_node(rp);
+ }
+
+ /* Free RIPng routing information. */
+ ripng_info_free(rinfo);
+}
+
+static void ripng_timeout_update(struct ripng *ripng, struct ripng_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 ripng_info *ripng_ecmp_add(struct ripng *ripng,
+ struct ripng_info *rinfo_new)
+{
+ struct agg_node *rp = rinfo_new->rp;
+ struct ripng_info *rinfo = NULL;
+ struct ripng_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) && !ripng->ecmp)
+ return NULL;
+
+ /* Add or replace an existing ECMP path with lower neighbor IP */
+ if (listcount(list) && listcount(list) >= ripng->ecmp) {
+ struct ripng_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 &&
+ IPV6_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 (ripng->ecmp > 1 && from_highest &&
+ IPV6_ADDR_CMP(&from_highest->from, &rinfo_new->from) > 0) {
+ ripng_ecmp_delete(ripng, from_highest);
+ goto add_or_replace;
+ }
+
+ return NULL;
+ }
+
+add_or_replace:
+ rinfo = ripng_info_new();
+ memcpy(rinfo, rinfo_new, sizeof(struct ripng_info));
+ listnode_add(list, rinfo);
+
+ if (ripng_route_rte(rinfo)) {
+ ripng_timeout_update(ripng, rinfo);
+ ripng_zebra_ipv6_add(ripng, rp);
+ }
+
+ ripng_aggregate_increment(rp, rinfo);
+
+ /* Set the route change flag on the first entry. */
+ rinfo = listgetdata(listhead(list));
+ SET_FLAG(rinfo->flags, RIPNG_RTF_CHANGED);
+
+ /* Signal the output process to trigger an update. */
+ ripng_event(ripng, RIPNG_TRIGGERED_UPDATE, 0);
+
+ return rinfo;
+}
+
+/* Update ECMP routes to zebra when `allow-ecmp` changed. */
+void ripng_ecmp_change(struct ripng *ripng)
+{
+ struct agg_node *rp;
+ struct ripng_info *rinfo;
+ struct list *list;
+ struct listnode *node, *nextnode;
+
+ for (rp = agg_route_top(ripng->table); rp; rp = agg_route_next(rp)) {
+ list = rp->info;
+ if (list && listcount(list) > 1) {
+ while (listcount(list) > ripng->ecmp) {
+ struct ripng_info *from_highest = NULL;
+
+ for (ALL_LIST_ELEMENTS(list, node, nextnode,
+ rinfo)) {
+ if (!from_highest ||
+ (from_highest &&
+ IPV6_ADDR_CMP(
+ &rinfo->from,
+ &from_highest->from) > 0))
+ from_highest = rinfo;
+ }
+
+ ripng_ecmp_delete(ripng, from_highest);
+ }
+ }
+ }
+}
+
+/* Replace the ECMP list with the new route.
+ * RETURN: the new entry added in the list
+ */
+struct ripng_info *ripng_ecmp_replace(struct ripng *ripng,
+ struct ripng_info *rinfo_new)
+{
+ struct agg_node *rp = rinfo_new->rp;
+ struct list *list = (struct list *)rp->info;
+ struct ripng_info *rinfo = NULL, *tmp_rinfo = NULL;
+ struct listnode *node = NULL, *nextnode = NULL;
+
+ if (list == NULL || listcount(list) == 0)
+ return ripng_ecmp_add(ripng, rinfo_new);
+
+ /* Get the first entry */
+ rinfo = listgetdata(listhead(list));
+
+ /* Learnt route replaced by a local one. Delete it from zebra. */
+ if (ripng_route_rte(rinfo) && !ripng_route_rte(rinfo_new))
+ if (CHECK_FLAG(rinfo->flags, RIPNG_RTF_FIB))
+ ripng_zebra_ipv6_delete(ripng, rp);
+
+ if (rinfo->metric != RIPNG_METRIC_INFINITY)
+ ripng_aggregate_decrement_list(rp, list);
+
+ /* Re-use the first entry, and delete the others. */
+ for (ALL_LIST_ELEMENTS(list, node, nextnode, tmp_rinfo))
+ if (tmp_rinfo != rinfo) {
+ EVENT_OFF(tmp_rinfo->t_timeout);
+ EVENT_OFF(tmp_rinfo->t_garbage_collect);
+ list_delete_node(list, node);
+ ripng_info_free(tmp_rinfo);
+ }
+
+ EVENT_OFF(rinfo->t_timeout);
+ EVENT_OFF(rinfo->t_garbage_collect);
+ memcpy(rinfo, rinfo_new, sizeof(struct ripng_info));
+
+ if (ripng_route_rte(rinfo)) {
+ ripng_timeout_update(ripng, rinfo);
+ /* The ADD message implies an update. */
+ ripng_zebra_ipv6_add(ripng, rp);
+ }
+
+ ripng_aggregate_increment(rp, rinfo);
+
+ /* Set the route change flag. */
+ SET_FLAG(rinfo->flags, RIPNG_RTF_CHANGED);
+
+ /* Signal the output process to trigger an update. */
+ ripng_event(ripng, RIPNG_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 ripng_info *ripng_ecmp_delete(struct ripng *ripng,
+ struct ripng_info *rinfo)
+{
+ struct agg_node *rp = rinfo->rp;
+ struct list *list = (struct list *)rp->info;
+
+ EVENT_OFF(rinfo->t_timeout);
+
+ if (rinfo->metric != RIPNG_METRIC_INFINITY)
+ ripng_aggregate_decrement(rp, rinfo);
+
+ 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 (ripng_route_rte(rinfo) &&
+ CHECK_FLAG(rinfo->flags, RIPNG_RTF_FIB))
+ /* The ADD message implies the update. */
+ ripng_zebra_ipv6_add(ripng, rp);
+ ripng_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 = RIPNG_METRIC_INFINITY;
+ RIPNG_TIMER_ON(rinfo->t_garbage_collect, ripng_garbage_collect,
+ ripng->garbage_time);
+
+ if (ripng_route_rte(rinfo) &&
+ CHECK_FLAG(rinfo->flags, RIPNG_RTF_FIB))
+ ripng_zebra_ipv6_delete(ripng, rp);
+ }
+
+ /* Set the route change flag on the first entry. */
+ rinfo = listgetdata(listhead(list));
+ SET_FLAG(rinfo->flags, RIPNG_RTF_CHANGED);
+
+ /* Signal the output process to trigger an update. */
+ ripng_event(ripng, RIPNG_TRIGGERED_UPDATE, 0);
+
+ return rinfo;
+}
+
+/* Timeout RIPng routes. */
+static void ripng_timeout(struct event *t)
+{
+ struct ripng_info *rinfo = EVENT_ARG(t);
+ struct ripng *ripng = ripng_info_get_instance(rinfo);
+
+ ripng_ecmp_delete(ripng, rinfo);
+}
+
+static void ripng_timeout_update(struct ripng *ripng, struct ripng_info *rinfo)
+{
+ if (rinfo->metric != RIPNG_METRIC_INFINITY) {
+ EVENT_OFF(rinfo->t_timeout);
+ event_add_timer(master, ripng_timeout, rinfo,
+ ripng->timeout_time, &rinfo->t_timeout);
+ }
+}
+
+static int ripng_filter(int ripng_distribute, struct prefix_ipv6 *p,
+ struct ripng_interface *ri)
+{
+ struct distribute *dist;
+ struct access_list *alist;
+ struct prefix_list *plist;
+ int distribute = ripng_distribute == RIPNG_FILTER_OUT
+ ? DISTRIBUTE_V6_OUT
+ : DISTRIBUTE_V6_IN;
+ const char *inout = ripng_distribute == RIPNG_FILTER_OUT ? "out" : "in";
+
+ /* Input distribute-list filtering. */
+ if (ri->list[ripng_distribute]) {
+ if (access_list_apply(ri->list[ripng_distribute],
+ (struct prefix *)p) == FILTER_DENY) {
+ if (IS_RIPNG_DEBUG_PACKET)
+ zlog_debug("%pFX filtered by distribute %s", p,
+ inout);
+ return -1;
+ }
+ }
+ if (ri->prefix[ripng_distribute]) {
+ if (prefix_list_apply(ri->prefix[ripng_distribute],
+ (struct prefix *)p) == PREFIX_DENY) {
+ if (IS_RIPNG_DEBUG_PACKET)
+ zlog_debug("%pFX filtered by prefix-list %s", p,
+ inout);
+ return -1;
+ }
+ }
+
+ /* All interface filter check. */
+ dist = distribute_lookup(ri->ripng->distribute_ctx, NULL);
+ if (dist) {
+ if (dist->list[distribute]) {
+ alist = access_list_lookup(AFI_IP6,
+ dist->list[distribute]);
+
+ if (alist) {
+ if (access_list_apply(alist, (struct prefix *)p)
+ == FILTER_DENY) {
+ if (IS_RIPNG_DEBUG_PACKET)
+ zlog_debug(
+ "%pFX filtered by distribute %s",
+ p, inout);
+ return -1;
+ }
+ }
+ }
+ if (dist->prefix[distribute]) {
+ plist = prefix_list_lookup(AFI_IP6,
+ dist->prefix[distribute]);
+
+ if (plist) {
+ if (prefix_list_apply(plist, (struct prefix *)p)
+ == PREFIX_DENY) {
+ if (IS_RIPNG_DEBUG_PACKET)
+ zlog_debug(
+ "%pFX filtered by prefix-list %s",
+ p, inout);
+ return -1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* Process RIPng route according to RFC2080. */
+static void ripng_route_process(struct rte *rte, struct sockaddr_in6 *from,
+ struct ripng_nexthop *ripng_nexthop,
+ struct interface *ifp)
+{
+ int ret;
+ struct prefix_ipv6 p;
+ struct agg_node *rp;
+ struct ripng_info *rinfo = NULL, newinfo;
+ struct ripng_interface *ri;
+ struct ripng *ripng;
+ struct in6_addr *nexthop;
+ int same = 0;
+ struct list *list = NULL;
+ struct listnode *node = NULL;
+
+ /* Make prefix structure. */
+ memset(&p, 0, sizeof(struct prefix_ipv6));
+ p.family = AF_INET6;
+ /* p.prefix = rte->addr; */
+ IPV6_ADDR_COPY(&p.prefix, &rte->addr);
+ p.prefixlen = rte->prefixlen;
+
+ /* Make sure mask is applied. */
+ /* XXX We have to check the prefix is valid or not before call
+ apply_mask_ipv6. */
+ apply_mask_ipv6(&p);
+
+ ri = ifp->info;
+ ripng = ri->ripng;
+
+ /* Apply input filters. */
+ ret = ripng_filter(RIPNG_FILTER_IN, &p, ri);
+ if (ret < 0)
+ return;
+
+ memset(&newinfo, 0, sizeof(newinfo));
+ newinfo.type = ZEBRA_ROUTE_RIPNG;
+ newinfo.sub_type = RIPNG_ROUTE_RTE;
+ if (ripng_nexthop->flag == RIPNG_NEXTHOP_ADDRESS)
+ newinfo.nexthop = ripng_nexthop->address;
+ else
+ newinfo.nexthop = from->sin6_addr;
+ newinfo.from = from->sin6_addr;
+ newinfo.ifindex = ifp->ifindex;
+ newinfo.metric = rte->metric;
+ newinfo.metric_out = rte->metric; /* XXX */
+ newinfo.tag = ntohs(rte->tag); /* XXX */
+
+ /* Modify entry. */
+ if (ri->routemap[RIPNG_FILTER_IN]) {
+ ret = route_map_apply(ri->routemap[RIPNG_FILTER_IN],
+ (struct prefix *)&p, &newinfo);
+
+ if (ret == RMAP_DENYMATCH) {
+ if (IS_RIPNG_DEBUG_PACKET)
+ zlog_debug(
+ "RIPng %pFX is filtered by route-map in",
+ &p);
+ return;
+ }
+
+ /* Get back the object */
+ if (ripng_nexthop->flag == RIPNG_NEXTHOP_ADDRESS) {
+ if (!IPV6_ADDR_SAME(&newinfo.nexthop,
+ &ripng_nexthop->address)) {
+ /* the nexthop get changed by the routemap */
+ if (IN6_IS_ADDR_LINKLOCAL(&newinfo.nexthop))
+ ripng_nexthop->address =
+ newinfo.nexthop;
+ else
+ ripng_nexthop->address = in6addr_any;
+ }
+ } else {
+ if (!IPV6_ADDR_SAME(&newinfo.nexthop,
+ &from->sin6_addr)) {
+ /* the nexthop get changed by the routemap */
+ if (IN6_IS_ADDR_LINKLOCAL(&newinfo.nexthop)) {
+ ripng_nexthop->flag =
+ RIPNG_NEXTHOP_ADDRESS;
+ ripng_nexthop->address =
+ newinfo.nexthop;
+ }
+ }
+ }
+ 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 wich the message
+ * arrived. If the result is greater than infinity, use infinity
+ * (RFC2453 Sec. 3.9.2)
+ **/
+
+ /* Zebra ripngd can handle offset-list in. */
+ ret = ripng_offset_list_apply_in(ripng, &p, ifp, &rte->metric);
+
+ /* If offset-list does not modify the metric use interface's
+ * one. */
+ if (!ret)
+ rte->metric += ifp->metric ? ifp->metric : 1;
+
+ if (rte->metric > RIPNG_METRIC_INFINITY)
+ rte->metric = RIPNG_METRIC_INFINITY;
+
+ /* Set nexthop pointer. */
+ if (ripng_nexthop->flag == RIPNG_NEXTHOP_ADDRESS)
+ nexthop = &ripng_nexthop->address;
+ else
+ nexthop = &from->sin6_addr;
+
+ /* Lookup RIPng routing table. */
+ rp = agg_node_get(ripng->table, (struct prefix *)&p);
+
+ newinfo.rp = rp;
+ newinfo.nexthop = *nexthop;
+ newinfo.metric = rte->metric;
+ newinfo.tag = ntohs(rte->tag);
+
+ /* Check to see whether there is already RIPng 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 (!ripng_route_rte(rinfo))
+ break;
+
+ if (IPV6_ADDR_SAME(&rinfo->from, &from->sin6_addr)
+ && IPV6_ADDR_SAME(&rinfo->nexthop, nexthop))
+ break;
+
+ if (!listnextnode(node)) {
+ /* Not found in the list */
+
+ if (rte->metric > rinfo->metric) {
+ /* New route has a greater metric.
+ * Discard it. */
+ agg_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. Unless ECMP is disabled,
+ * keep "rinfo" null and
+ * the new route is added in the ECMP list in
+ * below. */
+ if (!ripng->ecmp)
+ break;
+ }
+ }
+
+ if (rinfo) {
+ /* Redistributed route check. */
+ if (rinfo->type != ZEBRA_ROUTE_RIPNG
+ && rinfo->metric != RIPNG_METRIC_INFINITY) {
+ agg_unlock_node(rp);
+ return;
+ }
+
+ /* Local static route. */
+ if (rinfo->type == ZEBRA_ROUTE_RIPNG
+ && ((rinfo->sub_type == RIPNG_ROUTE_STATIC)
+ || (rinfo->sub_type == RIPNG_ROUTE_DEFAULT))
+ && rinfo->metric != RIPNG_METRIC_INFINITY) {
+ agg_unlock_node(rp);
+ return;
+ }
+ }
+
+ if (!rinfo) {
+ /* 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 != RIPNG_METRIC_INFINITY)
+ ripng_ecmp_add(ripng, &newinfo);
+ else
+ agg_unlock_node(rp);
+ } else {
+ /* 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 = (IN6_ARE_ADDR_EQUAL(&rinfo->from, &from->sin6_addr)
+ && (rinfo->ifindex == ifp->ifindex));
+
+ /*
+ * RFC 2080 - Section 2.4.2:
+ * "If the new metric is the same as the old one, examine the
+ * timeout
+ * for the existing route. If it is at least halfway to the
+ * expiration
+ * point, switch to the new route. This heuristic is optional,
+ * but
+ * highly recommended".
+ */
+ if (!ripng->ecmp && !same && rinfo->metric == rte->metric &&
+ rinfo->t_timeout &&
+ (event_timer_remain_second(rinfo->t_timeout) <
+ (ripng->timeout_time / 2))) {
+ ripng_ecmp_replace(ripng, &newinfo);
+ }
+ /* 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; do the following actions: */
+ else if ((same && rinfo->metric != rte->metric) ||
+ rte->metric < rinfo->metric) {
+ if (listcount(list) == 1) {
+ if (newinfo.metric != RIPNG_METRIC_INFINITY)
+ ripng_ecmp_replace(ripng, &newinfo);
+ else
+ ripng_ecmp_delete(ripng, rinfo);
+ } else {
+ if (newinfo.metric < rinfo->metric)
+ ripng_ecmp_replace(ripng, &newinfo);
+ else /* newinfo.metric > rinfo->metric */
+ ripng_ecmp_delete(ripng, rinfo);
+ }
+ } else /* same & no change */
+ ripng_timeout_update(ripng, rinfo);
+
+ /* Unlock tempolary lock of the route. */
+ agg_unlock_node(rp);
+ }
+}
+
+/* Add redistributed route to RIPng table. */
+void ripng_redistribute_add(struct ripng *ripng, int type, int sub_type,
+ struct prefix_ipv6 *p, ifindex_t ifindex,
+ struct in6_addr *nexthop, route_tag_t tag)
+{
+ struct agg_node *rp;
+ struct ripng_info *rinfo = NULL, newinfo;
+ struct list *list = NULL;
+
+ /* Redistribute route */
+ if (IN6_IS_ADDR_LINKLOCAL(&p->prefix))
+ return;
+ if (IN6_IS_ADDR_LOOPBACK(&p->prefix))
+ return;
+
+ rp = agg_node_get(ripng->table, (struct prefix *)p);
+
+ memset(&newinfo, 0, sizeof(newinfo));
+ newinfo.type = type;
+ newinfo.sub_type = sub_type;
+ newinfo.ifindex = ifindex;
+ newinfo.metric = 1;
+ if (tag <= UINT16_MAX) /* RIPng only supports 16 bit tags */
+ newinfo.tag = tag;
+ newinfo.rp = rp;
+ if (nexthop && IN6_IS_ADDR_LINKLOCAL(nexthop))
+ newinfo.nexthop = *nexthop;
+
+ if ((list = rp->info) != NULL && listcount(list) != 0) {
+ rinfo = listgetdata(listhead(list));
+
+ if (rinfo->type == ZEBRA_ROUTE_CONNECT
+ && rinfo->sub_type == RIPNG_ROUTE_INTERFACE
+ && rinfo->metric != RIPNG_METRIC_INFINITY) {
+ agg_unlock_node(rp);
+ return;
+ }
+
+ /* Manually configured RIPng route check.
+ * They have the precedence on all the other entries.
+ **/
+ if (rinfo->type == ZEBRA_ROUTE_RIPNG
+ && ((rinfo->sub_type == RIPNG_ROUTE_STATIC)
+ || (rinfo->sub_type == RIPNG_ROUTE_DEFAULT))) {
+ if (type != ZEBRA_ROUTE_RIPNG
+ || ((sub_type != RIPNG_ROUTE_STATIC)
+ && (sub_type != RIPNG_ROUTE_DEFAULT))) {
+ agg_unlock_node(rp);
+ return;
+ }
+ }
+
+ ripng_ecmp_replace(ripng, &newinfo);
+ agg_unlock_node(rp);
+ } else
+ ripng_ecmp_add(ripng, &newinfo);
+
+ if (IS_RIPNG_DEBUG_EVENT) {
+ if (!nexthop)
+ zlog_debug(
+ "Redistribute new prefix %pFX on the interface %s",
+ p, ifindex2ifname(ifindex, ripng->vrf->vrf_id));
+ else
+ zlog_debug(
+ "Redistribute new prefix %pFX with nexthop %pI6 on the interface %s",
+ p, nexthop,
+ ifindex2ifname(ifindex, ripng->vrf->vrf_id));
+ }
+
+ ripng_event(ripng, RIPNG_TRIGGERED_UPDATE, 0);
+}
+
+/* Delete redistributed route to RIPng table. */
+void ripng_redistribute_delete(struct ripng *ripng, int type, int sub_type,
+ struct prefix_ipv6 *p, ifindex_t ifindex)
+{
+ struct agg_node *rp;
+ struct ripng_info *rinfo;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&p->prefix))
+ return;
+ if (IN6_IS_ADDR_LOOPBACK(&p->prefix))
+ return;
+
+ rp = agg_node_lookup(ripng->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->ifindex == ifindex) {
+ /* Perform poisoned reverse. */
+ rinfo->metric = RIPNG_METRIC_INFINITY;
+ RIPNG_TIMER_ON(rinfo->t_garbage_collect,
+ ripng_garbage_collect,
+ ripng->garbage_time);
+ EVENT_OFF(rinfo->t_timeout);
+
+ /* Aggregate count decrement. */
+ ripng_aggregate_decrement(rp, rinfo);
+
+ SET_FLAG(rinfo->flags, RIPNG_RTF_CHANGED);
+
+ if (IS_RIPNG_DEBUG_EVENT)
+ zlog_debug(
+ "Poisone %pFX on the interface %s with an infinity metric [delete]",
+ p,
+ ifindex2ifname(
+ ifindex,
+ ripng->vrf->vrf_id));
+
+ ripng_event(ripng, RIPNG_TRIGGERED_UPDATE, 0);
+ }
+ }
+ agg_unlock_node(rp);
+ }
+}
+
+/* Withdraw redistributed route. */
+void ripng_redistribute_withdraw(struct ripng *ripng, int type)
+{
+ struct agg_node *rp;
+ struct ripng_info *rinfo = NULL;
+ struct list *list = NULL;
+
+ for (rp = agg_route_top(ripng->table); rp; rp = agg_route_next(rp))
+ if ((list = rp->info) != NULL) {
+ rinfo = listgetdata(listhead(list));
+ if ((rinfo->type == type)
+ && (rinfo->sub_type != RIPNG_ROUTE_INTERFACE)) {
+ /* Perform poisoned reverse. */
+ rinfo->metric = RIPNG_METRIC_INFINITY;
+ RIPNG_TIMER_ON(rinfo->t_garbage_collect,
+ ripng_garbage_collect,
+ ripng->garbage_time);
+ EVENT_OFF(rinfo->t_timeout);
+
+ /* Aggregate count decrement. */
+ ripng_aggregate_decrement(rp, rinfo);
+
+ SET_FLAG(rinfo->flags, RIPNG_RTF_CHANGED);
+
+ if (IS_RIPNG_DEBUG_EVENT) {
+ struct prefix_ipv6 *p =
+ (struct prefix_ipv6 *)
+ agg_node_get_prefix(rp);
+
+ zlog_debug(
+ "Poisone %pFX on the interface %s [withdraw]",
+ p,
+ ifindex2ifname(
+ rinfo->ifindex,
+ ripng->vrf->vrf_id));
+ }
+
+ ripng_event(ripng, RIPNG_TRIGGERED_UPDATE, 0);
+ }
+ }
+}
+
+/* RIP routing information. */
+static void ripng_response_process(struct ripng_packet *packet, int size,
+ struct sockaddr_in6 *from,
+ struct interface *ifp, int hoplimit)
+{
+ struct ripng_interface *ri = ifp->info;
+ struct ripng *ripng = ri->ripng;
+ caddr_t lim;
+ struct rte *rte;
+ struct ripng_nexthop nexthop;
+
+ /* RFC2080 2.4.2 Response Messages:
+ The Response must be ignored if it is not from the RIPng port. */
+ if (ntohs(from->sin6_port) != RIPNG_PORT_DEFAULT) {
+ zlog_warn("RIPng packet comes from non RIPng port %d from %pI6",
+ ntohs(from->sin6_port), &from->sin6_addr);
+ ripng_peer_bad_packet(ripng, from);
+ return;
+ }
+
+ /* The datagram's IPv6 source address should be checked to see
+ whether the datagram is from a valid neighbor; the source of the
+ datagram must be a link-local address. */
+ if (!IN6_IS_ADDR_LINKLOCAL(&from->sin6_addr)) {
+ zlog_warn("RIPng packet comes from non link local address %pI6",
+ &from->sin6_addr);
+ ripng_peer_bad_packet(ripng, from);
+ return;
+ }
+
+ /* It is also worth checking to see whether the response is from one
+ of the router's own addresses. Interfaces on broadcast networks
+ may receive copies of their own multicasts immediately. If a
+ router processes its own output as new input, confusion is likely,
+ and such datagrams must be ignored. */
+ if (ripng_lladdr_check(ifp, &from->sin6_addr)) {
+ zlog_warn(
+ "RIPng packet comes from my own link local address %pI6",
+ &from->sin6_addr);
+ ripng_peer_bad_packet(ripng, from);
+ return;
+ }
+
+ /* As an additional check, periodic advertisements must have their
+ hop counts set to 255, and inbound, multicast packets sent from the
+ RIPng port (i.e. periodic advertisement or triggered update
+ packets) must be examined to ensure that the hop count is 255. */
+ if (hoplimit >= 0 && hoplimit != 255) {
+ zlog_warn(
+ "RIPng packet comes with non 255 hop count %d from %pI6",
+ hoplimit, &from->sin6_addr);
+ ripng_peer_bad_packet(ripng, from);
+ return;
+ }
+
+ /* Update RIPng peer. */
+ ripng_peer_update(ripng, from, packet->version);
+
+ /* Reset nexthop. */
+ memset(&nexthop, 0, sizeof(nexthop));
+ nexthop.flag = RIPNG_NEXTHOP_UNSPEC;
+
+ /* Set RTE pointer. */
+ rte = packet->rte;
+
+ for (lim = ((caddr_t)packet) + size; (caddr_t)rte < lim; rte++) {
+ /* First of all, we have to check this RTE is next hop RTE or
+ not. Next hop RTE is completely different with normal RTE so
+ we need special treatment. */
+ if (rte->metric == RIPNG_METRIC_NEXTHOP) {
+ ripng_nexthop_rte(rte, from, &nexthop);
+ continue;
+ }
+
+ /* RTE information validation. */
+
+ /* - is the destination prefix valid (e.g., not a multicast
+ prefix and not a link-local address) A link-local address
+ should never be present in an RTE. */
+ if (IN6_IS_ADDR_MULTICAST(&rte->addr)) {
+ zlog_warn(
+ "Destination prefix is a multicast address %pI6/%d [%d]",
+ &rte->addr, rte->prefixlen, rte->metric);
+ ripng_peer_bad_route(ripng, from);
+ continue;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&rte->addr)) {
+ zlog_warn(
+ "Destination prefix is a link-local address %pI6/%d [%d]",
+ &rte->addr, rte->prefixlen, rte->metric);
+ ripng_peer_bad_route(ripng, from);
+ continue;
+ }
+ if (IN6_IS_ADDR_LOOPBACK(&rte->addr)) {
+ zlog_warn(
+ "Destination prefix is a loopback address %pI6/%d [%d]",
+ &rte->addr, rte->prefixlen, rte->metric);
+ ripng_peer_bad_route(ripng, from);
+ continue;
+ }
+
+ /* - is the prefix length valid (i.e., between 0 and 128,
+ inclusive) */
+ if (rte->prefixlen > IPV6_MAX_BITLEN) {
+ zlog_warn("Invalid prefix length %pI6/%d from %pI6%%%s",
+ &rte->addr, rte->prefixlen,
+ &from->sin6_addr, ifp->name);
+ ripng_peer_bad_route(ripng, from);
+ continue;
+ }
+
+ /* - is the metric valid (i.e., between 1 and 16, inclusive) */
+ if (!(rte->metric >= 1 && rte->metric <= 16)) {
+ zlog_warn("Invalid metric %d from %pI6%%%s",
+ rte->metric, &from->sin6_addr, ifp->name);
+ ripng_peer_bad_route(ripng, from);
+ continue;
+ }
+
+ /* Vincent: XXX Should we compute the direclty reachable nexthop
+ * for our RIPng network ?
+ **/
+
+ /* Routing table updates. */
+ ripng_route_process(rte, from, &nexthop, ifp);
+ }
+}
+
+/* Response to request message. */
+static void ripng_request_process(struct ripng_packet *packet, int size,
+ struct sockaddr_in6 *from,
+ struct interface *ifp)
+{
+ struct ripng *ripng;
+ caddr_t lim;
+ struct rte *rte;
+ struct prefix_ipv6 p;
+ struct agg_node *rp;
+ struct ripng_info *rinfo;
+ struct ripng_interface *ri;
+
+ /* Does not reponse to the requests on the loopback interfaces */
+ if (if_is_loopback(ifp))
+ return;
+
+ /* Check RIPng process is enabled on this interface. */
+ ri = ifp->info;
+ if (!ri->running)
+ return;
+ ripng = ri->ripng;
+
+ /* When passive interface is specified, suppress responses */
+ if (ri->passive)
+ return;
+
+ /* RIPng peer update. */
+ ripng_peer_update(ripng, 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 a destination prefix of zero, a prefix length
+ of zero, and a metric of infinity (i.e., 16), then this is a
+ request to send the entire routing table. In that case, a call
+ is made to the output process to send the routing table to the
+ requesting address/port. */
+ if (lim == ((caddr_t)(rte + 1)) && IN6_IS_ADDR_UNSPECIFIED(&rte->addr)
+ && rte->prefixlen == 0 && rte->metric == RIPNG_METRIC_INFINITY) {
+ /* All route with split horizon */
+ ripng_output_process(ifp, from, ripng_all_route);
+ } else {
+ /* Except for this special case, processing is quite simple.
+ 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. */
+ memset(&p, 0, sizeof(p));
+ p.family = AF_INET6;
+
+ for (; ((caddr_t)rte) < lim; rte++) {
+ p.prefix = rte->addr;
+ p.prefixlen = rte->prefixlen;
+ apply_mask_ipv6(&p);
+
+ rp = agg_node_lookup(ripng->table, (struct prefix *)&p);
+
+ if (rp) {
+ rinfo = listgetdata(
+ listhead((struct list *)rp->info));
+ rte->metric = rinfo->metric;
+ agg_unlock_node(rp);
+ } else
+ rte->metric = RIPNG_METRIC_INFINITY;
+ }
+ packet->command = RIPNG_RESPONSE;
+
+ ripng_send_packet((caddr_t)packet, size, from, ifp);
+ }
+}
+
+/* First entry point of reading RIPng packet. */
+static void ripng_read(struct event *thread)
+{
+ struct ripng *ripng = EVENT_ARG(thread);
+ int len;
+ int sock;
+ struct sockaddr_in6 from;
+ struct ripng_packet *packet;
+ ifindex_t ifindex = 0;
+ struct interface *ifp;
+ int hoplimit = -1;
+
+ /* Check ripng is active and alive. */
+ assert(ripng != NULL);
+ assert(ripng->sock >= 0);
+
+ /* Fetch thread data and set read pointer to empty for event
+ managing. `sock' sould be same as ripng->sock. */
+ sock = EVENT_FD(thread);
+
+ /* Add myself to the next event. */
+ ripng_event(ripng, RIPNG_READ, sock);
+
+ /* Read RIPng packet. */
+ len = ripng_recv_packet(sock, STREAM_DATA(ripng->ibuf),
+ STREAM_SIZE(ripng->ibuf), &from, &ifindex,
+ &hoplimit);
+ if (len < 0) {
+ zlog_warn("RIPng recvfrom failed (VRF %s): %s.",
+ ripng->vrf_name, safe_strerror(errno));
+ return;
+ }
+
+ /* Check RTE boundary. RTE size (Packet length - RIPng header size
+ (4)) must be multiple size of one RTE size (20). */
+ if (((len - 4) % 20) != 0) {
+ zlog_warn("RIPng invalid packet size %d from %pI6 (VRF %s)",
+ len, &from.sin6_addr, ripng->vrf_name);
+ ripng_peer_bad_packet(ripng, &from);
+ return;
+ }
+
+ packet = (struct ripng_packet *)STREAM_DATA(ripng->ibuf);
+ ifp = if_lookup_by_index(ifindex, ripng->vrf->vrf_id);
+
+ /* RIPng packet received. */
+ if (IS_RIPNG_DEBUG_EVENT)
+ zlog_debug(
+ "RIPng packet received from %pI6 port %d on %s (VRF %s)",
+ &from.sin6_addr, ntohs(from.sin6_port),
+ ifp ? ifp->name : "unknown", ripng->vrf_name);
+
+ /* Logging before packet checking. */
+ if (IS_RIPNG_DEBUG_RECV)
+ ripng_packet_dump(packet, len, "RECV");
+
+ /* Packet comes from unknown interface. */
+ if (ifp == NULL) {
+ zlog_warn(
+ "RIPng packet comes from unknown interface %d (VRF %s)",
+ ifindex, ripng->vrf_name);
+ return;
+ }
+
+ /* Packet version mismatch checking. */
+ if (packet->version != ripng->version) {
+ zlog_warn(
+ "RIPng packet version %d doesn't fit to my version %d (VRF %s)",
+ packet->version, ripng->version, ripng->vrf_name);
+ ripng_peer_bad_packet(ripng, &from);
+ return;
+ }
+
+ /* Process RIPng packet. */
+ switch (packet->command) {
+ case RIPNG_REQUEST:
+ ripng_request_process(packet, len, &from, ifp);
+ break;
+ case RIPNG_RESPONSE:
+ ripng_response_process(packet, len, &from, ifp, hoplimit);
+ break;
+ default:
+ zlog_warn("Invalid RIPng command %d (VRF %s)", packet->command,
+ ripng->vrf_name);
+ ripng_peer_bad_packet(ripng, &from);
+ break;
+ }
+}
+
+/* Walk down the RIPng routing table then clear changed flag. */
+static void ripng_clear_changed_flag(struct ripng *ripng)
+{
+ struct agg_node *rp;
+ struct ripng_info *rinfo = NULL;
+ struct list *list = NULL;
+ struct listnode *listnode = NULL;
+
+ for (rp = agg_route_top(ripng->table); rp; rp = agg_route_next(rp))
+ if ((list = rp->info) != NULL)
+ for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) {
+ UNSET_FLAG(rinfo->flags, RIPNG_RTF_CHANGED);
+ /* This flag can be set only on the first entry.
+ */
+ break;
+ }
+}
+
+/* Regular update of RIPng route. Send all routing formation to RIPng
+ enabled interface. */
+static void ripng_update(struct event *t)
+{
+ struct ripng *ripng = EVENT_ARG(t);
+ struct interface *ifp;
+ struct ripng_interface *ri;
+
+ /* Logging update event. */
+ if (IS_RIPNG_DEBUG_EVENT)
+ zlog_debug("RIPng update timer expired!");
+
+ /* Supply routes to each interface. */
+ FOR_ALL_INTERFACES (ripng->vrf, ifp) {
+ ri = ifp->info;
+
+ if (if_is_loopback(ifp) || !if_is_up(ifp))
+ continue;
+
+ if (!ri->running)
+ continue;
+
+ /* When passive interface is specified, suppress announce to the
+ interface. */
+ if (ri->passive)
+ continue;
+
+#ifdef RIPNG_ADVANCED
+ if (ri->ri_send == RIPNG_SEND_OFF) {
+ if (IS_RIPNG_DEBUG_EVENT)
+ zlog_debug(
+ "[Event] RIPng send to if %d is suppressed by config",
+ ifp->ifindex);
+ continue;
+ }
+#endif /* RIPNG_ADVANCED */
+
+ ripng_output_process(ifp, NULL, ripng_all_route);
+ }
+
+ /* Triggered updates may be suppressed if a regular update is due by
+ the time the triggered update would be sent. */
+ EVENT_OFF(ripng->t_triggered_interval);
+ ripng->trigger = 0;
+
+ /* Reset flush event. */
+ ripng_event(ripng, RIPNG_UPDATE_EVENT, 0);
+}
+
+/* Triggered update interval timer. */
+static void ripng_triggered_interval(struct event *t)
+{
+ struct ripng *ripng = EVENT_ARG(t);
+
+ if (ripng->trigger) {
+ ripng->trigger = 0;
+ ripng_triggered_update(t);
+ }
+}
+
+/* Execute triggered update. */
+void ripng_triggered_update(struct event *t)
+{
+ struct ripng *ripng = EVENT_ARG(t);
+ struct interface *ifp;
+ struct ripng_interface *ri;
+ int interval;
+
+ /* Cancel interval timer. */
+ EVENT_OFF(ripng->t_triggered_interval);
+ ripng->trigger = 0;
+
+ /* Logging triggered update. */
+ if (IS_RIPNG_DEBUG_EVENT)
+ zlog_debug("RIPng triggered update!");
+
+ /* Split Horizon processing is done when generating triggered
+ updates as well as normal updates (see section 2.6). */
+ FOR_ALL_INTERFACES (ripng->vrf, ifp) {
+ ri = ifp->info;
+
+ if (if_is_loopback(ifp) || !if_is_up(ifp))
+ continue;
+
+ if (!ri->running)
+ continue;
+
+ /* When passive interface is specified, suppress announce to the
+ interface. */
+ if (ri->passive)
+ continue;
+
+ ripng_output_process(ifp, NULL, ripng_changed_route);
+ }
+
+ /* Once all of the triggered updates have been generated, the route
+ change flags should be cleared. */
+ ripng_clear_changed_flag(ripng);
+
+ /* 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, ripng_triggered_interval, ripng, interval,
+ &ripng->t_triggered_interval);
+}
+
+/* Write routing table entry to the stream and return next index of
+ the routing table entry in the stream. */
+int ripng_write_rte(int num, struct stream *s, struct prefix_ipv6 *p,
+ struct in6_addr *nexthop, uint16_t tag, uint8_t metric)
+{
+ /* RIPng packet header. */
+ if (num == 0) {
+ stream_putc(s, RIPNG_RESPONSE);
+ stream_putc(s, RIPNG_V1);
+ stream_putw(s, 0);
+ }
+
+ /* Write routing table entry. */
+ if (!nexthop) {
+ assert(p);
+ stream_write(s, (uint8_t *)&p->prefix, sizeof(struct in6_addr));
+ } else
+ stream_write(s, (uint8_t *)nexthop, sizeof(struct in6_addr));
+ stream_putw(s, tag);
+ if (p)
+ stream_putc(s, p->prefixlen);
+ else
+ stream_putc(s, 0);
+ stream_putc(s, metric);
+
+ return ++num;
+}
+
+/* Send RESPONSE message to specified destination. */
+void ripng_output_process(struct interface *ifp, struct sockaddr_in6 *to,
+ int route_type)
+{
+ struct ripng *ripng;
+ int ret;
+ struct agg_node *rp;
+ struct ripng_info *rinfo;
+ struct ripng_interface *ri;
+ struct ripng_aggregate *aggregate;
+ struct prefix_ipv6 *p;
+ struct list *ripng_rte_list;
+ struct list *list = NULL;
+ struct listnode *listnode = NULL;
+
+ if (IS_RIPNG_DEBUG_EVENT) {
+ if (to)
+ zlog_debug("RIPng update routes to neighbor %pI6",
+ &to->sin6_addr);
+ else
+ zlog_debug("RIPng update routes on interface %s",
+ ifp->name);
+ }
+
+ /* Get RIPng interface and instance. */
+ ri = ifp->info;
+ ripng = ri->ripng;
+
+ ripng_rte_list = ripng_rte_new();
+
+ for (rp = agg_route_top(ripng->table); rp; rp = agg_route_next(rp)) {
+ if ((list = rp->info) != NULL
+ && (rinfo = listgetdata(listhead(list))) != NULL
+ && rinfo->suppress == 0) {
+ /* If no route-map are applied, the RTE will be these
+ * following
+ * information.
+ */
+ p = (struct prefix_ipv6 *)agg_node_get_prefix(rp);
+ rinfo->metric_out = rinfo->metric;
+ rinfo->tag_out = rinfo->tag;
+ memset(&rinfo->nexthop_out, 0,
+ sizeof(rinfo->nexthop_out));
+ /* In order to avoid some local loops,
+ * if the RIPng 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.
+ */
+ if (rinfo->ifindex == ifp->ifindex)
+ rinfo->nexthop_out = rinfo->nexthop;
+
+ /* Apply output filters. */
+ ret = ripng_filter(RIPNG_FILTER_OUT, p, ri);
+ if (ret < 0)
+ continue;
+
+ /* Changed route only output. */
+ if (route_type == ripng_changed_route &&
+ (!CHECK_FLAG(rinfo->flags, RIPNG_RTF_CHANGED)))
+ continue;
+
+ /* Split horizon. */
+ if (ri->split_horizon == RIPNG_SPLIT_HORIZON) {
+ /* We perform split horizon for RIPng routes. */
+ int suppress = 0;
+ struct ripng_info *tmp_rinfo = NULL;
+
+ for (ALL_LIST_ELEMENTS_RO(list, listnode,
+ tmp_rinfo))
+ if (tmp_rinfo->type ==
+ ZEBRA_ROUTE_RIPNG &&
+ tmp_rinfo->ifindex ==
+ ifp->ifindex) {
+ suppress = 1;
+ break;
+ }
+ if (suppress)
+ continue;
+ }
+
+ /* Preparation for route-map. */
+ rinfo->metric_set = 0;
+ /* nexthop_out,
+ * metric_out
+ * and tag_out are already initialized.
+ */
+
+ /* Interface route-map */
+ if (ri->routemap[RIPNG_FILTER_OUT]) {
+ ret = route_map_apply(
+ ri->routemap[RIPNG_FILTER_OUT],
+ (struct prefix *)p, rinfo);
+
+ if (ret == RMAP_DENYMATCH) {
+ if (IS_RIPNG_DEBUG_PACKET)
+ zlog_debug(
+ "RIPng %pFX is filtered by route-map out",
+ p);
+ continue;
+ }
+ }
+
+ /* Redistribute route-map. */
+ if (ripng->redist[rinfo->type].route_map.name) {
+ ret = route_map_apply(ripng->redist[rinfo->type]
+ .route_map.map,
+ (struct prefix *)p,
+ rinfo);
+
+ if (ret == RMAP_DENYMATCH) {
+ if (IS_RIPNG_DEBUG_PACKET)
+ zlog_debug(
+ "RIPng %pFX is filtered by route-map",
+ p);
+ continue;
+ }
+ }
+
+ /* When the route-map does not set metric. */
+ if (!rinfo->metric_set) {
+ /* If the redistribute metric is set. */
+ if (ripng->redist[rinfo->type].metric_config
+ && rinfo->metric != RIPNG_METRIC_INFINITY) {
+ rinfo->metric_out =
+ ripng->redist[rinfo->type]
+ .metric;
+ } else {
+ /* If the route is not connected or
+ localy generated
+ one, use default-metric value */
+ if (rinfo->type != ZEBRA_ROUTE_RIPNG &&
+ rinfo->type !=
+ ZEBRA_ROUTE_CONNECT &&
+ rinfo->metric !=
+ RIPNG_METRIC_INFINITY)
+ rinfo->metric_out =
+ ripng->default_metric;
+ }
+ }
+
+ /* Apply offset-list */
+ if (rinfo->metric_out != RIPNG_METRIC_INFINITY)
+ ripng_offset_list_apply_out(ripng, p, ifp,
+ &rinfo->metric_out);
+
+ if (rinfo->metric_out > RIPNG_METRIC_INFINITY)
+ rinfo->metric_out = RIPNG_METRIC_INFINITY;
+
+ /* Perform split-horizon with poisoned reverse
+ * for RIPng routes.
+ **/
+ if (ri->split_horizon ==
+ RIPNG_SPLIT_HORIZON_POISONED_REVERSE) {
+ struct ripng_info *tmp_rinfo = NULL;
+
+ for (ALL_LIST_ELEMENTS_RO(list, listnode,
+ tmp_rinfo))
+ if ((tmp_rinfo->type ==
+ ZEBRA_ROUTE_RIPNG) &&
+ tmp_rinfo->ifindex == ifp->ifindex)
+ rinfo->metric_out =
+ RIPNG_METRIC_INFINITY;
+ }
+
+ /* Add RTE to the list */
+ ripng_rte_add(ripng_rte_list, p, rinfo, NULL);
+ }
+
+ /* Process the aggregated RTE entry */
+ if ((aggregate = rp->aggregate) != NULL && aggregate->count > 0
+ && aggregate->suppress == 0) {
+ /* If no route-map are applied, the RTE will be these
+ * following
+ * information.
+ */
+ p = (struct prefix_ipv6 *)agg_node_get_prefix(rp);
+ aggregate->metric_set = 0;
+ aggregate->metric_out = aggregate->metric;
+ aggregate->tag_out = aggregate->tag;
+ memset(&aggregate->nexthop_out, 0,
+ sizeof(aggregate->nexthop_out));
+
+ /* Apply output filters.*/
+ ret = ripng_filter(RIPNG_FILTER_OUT, p, ri);
+ if (ret < 0)
+ continue;
+
+ /* Interface route-map */
+ if (ri->routemap[RIPNG_FILTER_OUT]) {
+ struct ripng_info newinfo;
+
+ /* let's cast the aggregate structure to
+ * ripng_info */
+ memset(&newinfo, 0, sizeof(struct ripng_info));
+ /* the nexthop is :: */
+ newinfo.metric = aggregate->metric;
+ newinfo.metric_out = aggregate->metric_out;
+ newinfo.tag = aggregate->tag;
+ newinfo.tag_out = aggregate->tag_out;
+
+ ret = route_map_apply(
+ ri->routemap[RIPNG_FILTER_OUT],
+ (struct prefix *)p, &newinfo);
+
+ if (ret == RMAP_DENYMATCH) {
+ if (IS_RIPNG_DEBUG_PACKET)
+ zlog_debug(
+ "RIPng %pFX is filtered by route-map out",
+ p);
+ continue;
+ }
+
+ aggregate->metric_out = newinfo.metric_out;
+ aggregate->tag_out = newinfo.tag_out;
+ if (IN6_IS_ADDR_LINKLOCAL(&newinfo.nexthop_out))
+ aggregate->nexthop_out =
+ newinfo.nexthop_out;
+ }
+
+ /* There is no redistribute routemap for the aggregated
+ * RTE */
+
+ /* Changed route only output. */
+ /* XXX, vincent, in order to increase time convergence,
+ * it should be announced if a child has changed.
+ */
+ if (route_type == ripng_changed_route)
+ continue;
+
+ /* Apply offset-list */
+ if (aggregate->metric_out != RIPNG_METRIC_INFINITY)
+ ripng_offset_list_apply_out(
+ ripng, p, ifp, &aggregate->metric_out);
+
+ if (aggregate->metric_out > RIPNG_METRIC_INFINITY)
+ aggregate->metric_out = RIPNG_METRIC_INFINITY;
+
+ /* Add RTE to the list */
+ ripng_rte_add(ripng_rte_list, p, NULL, aggregate);
+ }
+ }
+
+ /* Flush the list */
+ ripng_rte_send(ripng_rte_list, ifp, to);
+ ripng_rte_free(ripng_rte_list);
+}
+
+struct ripng *ripng_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 ripng *ripng_lookup_by_vrf_name(const char *vrf_name)
+{
+ struct ripng ripng;
+
+ ripng.vrf_name = (char *)vrf_name;
+
+ return RB_FIND(ripng_instance_head, &ripng_instances, &ripng);
+}
+
+/* Create new RIPng instance and set it to global variable. */
+struct ripng *ripng_create(const char *vrf_name, struct vrf *vrf, int socket)
+{
+ struct ripng *ripng;
+
+ /* Allocaste RIPng instance. */
+ ripng = XCALLOC(MTYPE_RIPNG, sizeof(struct ripng));
+ ripng->vrf_name = XSTRDUP(MTYPE_RIPNG_VRF_NAME, vrf_name);
+
+ /* Default version and timer values. */
+ ripng->version = RIPNG_V1;
+ ripng->update_time = yang_get_default_uint32(
+ "%s/timers/update-interval", RIPNG_INSTANCE);
+ ripng->timeout_time = yang_get_default_uint32(
+ "%s/timers/holddown-interval", RIPNG_INSTANCE);
+ ripng->garbage_time = yang_get_default_uint32(
+ "%s/timers/flush-interval", RIPNG_INSTANCE);
+ ripng->default_metric =
+ yang_get_default_uint8("%s/default-metric", RIPNG_INSTANCE);
+ ripng->ecmp = yang_get_default_uint8("%s/allow-ecmp", RIPNG_INSTANCE);
+
+ /* Make buffer. */
+ ripng->ibuf = stream_new(RIPNG_MAX_PACKET_SIZE * 5);
+ ripng->obuf = stream_new(RIPNG_MAX_PACKET_SIZE);
+
+ /* Initialize RIPng data structures. */
+ ripng->table = agg_table_init();
+ agg_set_table_info(ripng->table, ripng);
+ ripng->peer_list = list_new();
+ ripng->peer_list->cmp = (int (*)(void *, void *))ripng_peer_list_cmp;
+ ripng->peer_list->del = ripng_peer_list_del;
+ ripng->enable_if = vector_init(1);
+ ripng->enable_network = agg_table_init();
+ ripng->passive_interface = vector_init(1);
+ ripng->offset_list_master = list_new();
+ ripng->offset_list_master->cmp =
+ (int (*)(void *, void *))offset_list_cmp;
+ ripng->offset_list_master->del =
+ (void (*)(void *))ripng_offset_list_free;
+ ripng->distribute_ctx = distribute_list_ctx_create(vrf);
+ distribute_list_add_hook(ripng->distribute_ctx,
+ ripng_distribute_update);
+ distribute_list_delete_hook(ripng->distribute_ctx,
+ ripng_distribute_update);
+
+ /* if rmap install. */
+ ripng->if_rmap_ctx = if_rmap_ctx_create(vrf_name);
+ if_rmap_hook_add(ripng->if_rmap_ctx, ripng_if_rmap_update);
+ if_rmap_hook_delete(ripng->if_rmap_ctx, ripng_if_rmap_update);
+
+ /* Enable the routing instance if possible. */
+ if (vrf && vrf_is_enabled(vrf))
+ ripng_instance_enable(ripng, vrf, socket);
+ else {
+ ripng->vrf = NULL;
+ ripng->sock = -1;
+ }
+
+ RB_INSERT(ripng_instance_head, &ripng_instances, ripng);
+
+ return ripng;
+}
+
+/* Send RIPng request to the interface. */
+int ripng_request(struct interface *ifp)
+{
+ struct rte *rte;
+ struct ripng_packet ripng_packet;
+
+ /* In default ripd doesn't send RIP_REQUEST to the loopback interface.
+ */
+ if (if_is_loopback(ifp))
+ return 0;
+
+ /* If interface is down, don't send RIP packet. */
+ if (!if_is_up(ifp))
+ return 0;
+
+ if (IS_RIPNG_DEBUG_EVENT)
+ zlog_debug("RIPng send request to %s", ifp->name);
+
+ memset(&ripng_packet, 0, sizeof(ripng_packet));
+ ripng_packet.command = RIPNG_REQUEST;
+ ripng_packet.version = RIPNG_V1;
+ rte = ripng_packet.rte;
+ rte->metric = RIPNG_METRIC_INFINITY;
+
+ return ripng_send_packet((caddr_t)&ripng_packet, sizeof(ripng_packet),
+ NULL, ifp);
+}
+
+
+static int ripng_update_jitter(int time)
+{
+ return ((frr_weak_random() % (time + 1)) - (time / 2));
+}
+
+void ripng_event(struct ripng *ripng, enum ripng_event event, int sock)
+{
+ int jitter = 0;
+
+ switch (event) {
+ case RIPNG_READ:
+ event_add_read(master, ripng_read, ripng, sock, &ripng->t_read);
+ break;
+ case RIPNG_UPDATE_EVENT:
+ EVENT_OFF(ripng->t_update);
+
+ /* Update timer jitter. */
+ jitter = ripng_update_jitter(ripng->update_time);
+
+ event_add_timer(master, ripng_update, ripng,
+ sock ? 2 : ripng->update_time + jitter,
+ &ripng->t_update);
+ break;
+ case RIPNG_TRIGGERED_UPDATE:
+ if (ripng->t_triggered_interval)
+ ripng->trigger = 1;
+ else
+ event_add_event(master, ripng_triggered_update, ripng,
+ 0, &ripng->t_triggered_update);
+ break;
+ case RIPNG_ZEBRA:
+ case RIPNG_REQUEST_EVENT:
+ break;
+ }
+}
+
+
+/* Print out routes update time. */
+static void ripng_vty_out_uptime(struct vty *vty, struct ripng_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 char *ripng_route_subtype_print(struct ripng_info *rinfo)
+{
+ static char str[3];
+ memset(str, 0, 3);
+
+ if (rinfo->suppress)
+ strlcat(str, "S", sizeof(str));
+
+ switch (rinfo->sub_type) {
+ case RIPNG_ROUTE_RTE:
+ strlcat(str, "n", sizeof(str));
+ break;
+ case RIPNG_ROUTE_STATIC:
+ strlcat(str, "s", sizeof(str));
+ break;
+ case RIPNG_ROUTE_DEFAULT:
+ strlcat(str, "d", sizeof(str));
+ break;
+ case RIPNG_ROUTE_REDISTRIBUTE:
+ strlcat(str, "r", sizeof(str));
+ break;
+ case RIPNG_ROUTE_INTERFACE:
+ strlcat(str, "i", sizeof(str));
+ break;
+ default:
+ strlcat(str, "?", sizeof(str));
+ break;
+ }
+
+ return str;
+}
+
+DEFUN (show_ipv6_ripng,
+ show_ipv6_ripng_cmd,
+ "show ipv6 ripng [vrf NAME]",
+ SHOW_STR
+ IPV6_STR
+ "Show RIPng routes\n"
+ VRF_CMD_HELP_STR)
+{
+ struct ripng *ripng;
+ struct agg_node *rp;
+ struct ripng_info *rinfo;
+ struct ripng_aggregate *aggregate;
+ struct list *list = NULL;
+ struct listnode *listnode = NULL;
+ int len;
+ 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;
+
+ ripng = ripng_lookup_by_vrf_name(vrf_name);
+ if (!ripng) {
+ vty_out(vty, "%% RIPng instance not found\n");
+ return CMD_SUCCESS;
+ }
+ if (!ripng->enabled) {
+ vty_out(vty, "%% RIPng instance is disabled\n");
+ return CMD_SUCCESS;
+ }
+
+ /* Header of display. */
+ vty_out(vty,
+ "Codes: R - RIPng, C - connected, S - Static, O - OSPF, B - BGP\n"
+ "Sub-codes:\n"
+ " (n) - normal, (s) - static, (d) - default, (r) - redistribute,\n"
+ " (i) - interface, (a/S) - aggregated/Suppressed\n\n"
+ " Network Next Hop Via Metric Tag Time\n");
+
+ for (rp = agg_route_top(ripng->table); rp; rp = agg_route_next(rp)) {
+ if ((aggregate = rp->aggregate) != NULL) {
+#ifdef DEBUG
+ vty_out(vty, "R(a) %d/%d %pRN ", aggregate->count,
+ aggregate->suppress, rp);
+#else
+ vty_out(vty, "R(a) %pRN ", rp);
+#endif /* DEBUG */
+ vty_out(vty, "\n");
+ vty_out(vty, "%*s", 18, " ");
+
+ vty_out(vty, "%*s", 28, " ");
+ vty_out(vty, "self %2d %3" ROUTE_TAG_PRI "\n",
+ aggregate->metric, (route_tag_t)aggregate->tag);
+ }
+
+ if ((list = rp->info) != NULL)
+ for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) {
+#ifdef DEBUG
+ vty_out(vty, "%c(%s) 0/%d %pRN ",
+ zebra_route_char(rinfo->type),
+ ripng_route_subtype_print(rinfo),
+ rinfo->suppress, rp);
+#else
+ vty_out(vty, "%c(%s) %pRN ",
+ zebra_route_char(rinfo->type),
+ ripng_route_subtype_print(rinfo), rp);
+#endif /* DEBUG */
+ vty_out(vty, "\n");
+ vty_out(vty, "%*s", 18, " ");
+ len = vty_out(vty, "%pI6",
+ &rinfo->nexthop);
+
+ len = 28 - len;
+ if (len > 0)
+ vty_out(vty, "%*s", len, " ");
+
+ /* from */
+ if ((rinfo->type == ZEBRA_ROUTE_RIPNG)
+ && (rinfo->sub_type == RIPNG_ROUTE_RTE)) {
+ len = vty_out(
+ vty, "%s",
+ ifindex2ifname(
+ rinfo->ifindex,
+ ripng->vrf->vrf_id));
+ } else if (rinfo->metric
+ == RIPNG_METRIC_INFINITY) {
+ len = vty_out(vty, "kill");
+ } else
+ len = vty_out(vty, "self");
+
+ len = 9 - len;
+ if (len > 0)
+ vty_out(vty, "%*s", len, " ");
+
+ vty_out(vty, " %2d %3" ROUTE_TAG_PRI " ",
+ rinfo->metric, (route_tag_t)rinfo->tag);
+
+ /* time */
+ if ((rinfo->type == ZEBRA_ROUTE_RIPNG)
+ && (rinfo->sub_type == RIPNG_ROUTE_RTE)) {
+ /* RTE from remote RIP routers */
+ ripng_vty_out_uptime(vty, rinfo);
+ } else if (rinfo->metric
+ == RIPNG_METRIC_INFINITY) {
+ /* poisonous reversed routes (gc) */
+ ripng_vty_out_uptime(vty, rinfo);
+ }
+
+ vty_out(vty, "\n");
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ipv6_ripng_status,
+ show_ipv6_ripng_status_cmd,
+ "show ipv6 ripng [vrf NAME] status",
+ SHOW_STR
+ IPV6_STR
+ "Show RIPng routes\n"
+ VRF_CMD_HELP_STR
+ "IPv6 routing protocol process parameters and statistics\n")
+{
+ struct ripng *ripng;
+ struct interface *ifp;
+ 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;
+
+ ripng = ripng_lookup_by_vrf_name(vrf_name);
+ if (!ripng) {
+ vty_out(vty, "%% RIPng instance not found\n");
+ return CMD_SUCCESS;
+ }
+ if (!ripng->enabled) {
+ vty_out(vty, "%% RIPng instance is disabled\n");
+ return CMD_SUCCESS;
+ }
+
+ vty_out(vty, "Routing Protocol is \"RIPng\"\n");
+ vty_out(vty, " Sending updates every %u seconds with +/-50%%,",
+ ripng->update_time);
+ vty_out(vty, " next due in %lu seconds\n",
+ event_timer_remain_second(ripng->t_update));
+ vty_out(vty, " Timeout after %u seconds,", ripng->timeout_time);
+ vty_out(vty, " garbage collect after %u seconds\n",
+ ripng->garbage_time);
+
+ /* Filtering status show. */
+ config_show_distribute(vty, ripng->distribute_ctx);
+
+ /* Default metric information. */
+ vty_out(vty, " Default redistribution metric is %d\n",
+ ripng->default_metric);
+
+ /* Redistribute information. */
+ vty_out(vty, " Redistributing:");
+ ripng_redistribute_write(vty, ripng);
+ vty_out(vty, "\n");
+
+ vty_out(vty, " Default version control: send version %d,",
+ ripng->version);
+ vty_out(vty, " receive version %d \n", ripng->version);
+
+ vty_out(vty, " Interface Send Recv\n");
+
+ FOR_ALL_INTERFACES (ripng->vrf, ifp) {
+ struct ripng_interface *ri;
+
+ ri = ifp->info;
+
+ if (ri->enable_network || ri->enable_interface) {
+
+ vty_out(vty, " %-17s%-3d %-3d\n", ifp->name,
+ ripng->version, ripng->version);
+ }
+ }
+
+ vty_out(vty, " Routing for Networks:\n");
+ ripng_network_write(vty, ripng);
+
+ vty_out(vty, " Routing Information Sources:\n");
+ vty_out(vty,
+ " Gateway BadPackets BadRoutes Distance Last Update\n");
+ ripng_peer_display(vty, ripng);
+
+ return CMD_SUCCESS;
+}
+
+/* Update ECMP routes to zebra when ECMP is disabled. */
+void ripng_ecmp_disable(struct ripng *ripng)
+{
+ struct agg_node *rp;
+ struct ripng_info *rinfo, *tmp_rinfo;
+ struct list *list;
+ struct listnode *node, *nextnode;
+
+ if (!ripng)
+ return;
+
+ for (rp = agg_route_top(ripng->table); rp; rp = agg_route_next(rp))
+ if ((list = rp->info) != NULL && listcount(list) > 1) {
+ rinfo = listgetdata(listhead(list));
+ if (!ripng_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) {
+ EVENT_OFF(tmp_rinfo->t_timeout);
+ EVENT_OFF(tmp_rinfo->t_garbage_collect);
+ list_delete_node(list, node);
+ ripng_info_free(tmp_rinfo);
+ }
+
+ /* Update zebra. */
+ ripng_zebra_ipv6_add(ripng, rp);
+
+ /* Set the route change flag. */
+ SET_FLAG(rinfo->flags, RIPNG_RTF_CHANGED);
+
+ /* Signal the output process to trigger an update. */
+ ripng_event(ripng, RIPNG_TRIGGERED_UPDATE, 0);
+ }
+}
+
+/* RIPng configuration write function. */
+static int ripng_config_write(struct vty *vty)
+{
+ struct ripng *ripng;
+ int write = 0;
+
+ RB_FOREACH(ripng, ripng_instance_head, &ripng_instances) {
+ char xpath[XPATH_MAXLEN];
+ struct lyd_node *dnode;
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-ripngd:ripngd/instance[vrf='%s']",
+ ripng->vrf_name);
+
+ dnode = yang_dnode_get(running_config->dnode, xpath);
+ assert(dnode);
+
+ nb_cli_show_dnode_cmds(vty, dnode, false);
+
+ config_write_distribute(vty, ripng->distribute_ctx);
+
+ vty_out(vty, "exit\n");
+
+ write = 1;
+ }
+
+ return write;
+}
+
+static int ripng_config_write(struct vty *vty);
+/* RIPng node structure. */
+static struct cmd_node cmd_ripng_node = {
+ .name = "ripng",
+ .node = RIPNG_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-router)# ",
+ .config_write = ripng_config_write,
+};
+
+static void ripng_distribute_update(struct distribute_ctx *ctx,
+ struct distribute *dist)
+{
+ struct interface *ifp;
+ struct ripng_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_V6_IN]) {
+ alist = access_list_lookup(AFI_IP6,
+ dist->list[DISTRIBUTE_V6_IN]);
+ if (alist)
+ ri->list[RIPNG_FILTER_IN] = alist;
+ else
+ ri->list[RIPNG_FILTER_IN] = NULL;
+ } else
+ ri->list[RIPNG_FILTER_IN] = NULL;
+
+ if (dist->list[DISTRIBUTE_V6_OUT]) {
+ alist = access_list_lookup(AFI_IP6,
+ dist->list[DISTRIBUTE_V6_OUT]);
+ if (alist)
+ ri->list[RIPNG_FILTER_OUT] = alist;
+ else
+ ri->list[RIPNG_FILTER_OUT] = NULL;
+ } else
+ ri->list[RIPNG_FILTER_OUT] = NULL;
+
+ if (dist->prefix[DISTRIBUTE_V6_IN]) {
+ plist = prefix_list_lookup(AFI_IP6,
+ dist->prefix[DISTRIBUTE_V6_IN]);
+ if (plist)
+ ri->prefix[RIPNG_FILTER_IN] = plist;
+ else
+ ri->prefix[RIPNG_FILTER_IN] = NULL;
+ } else
+ ri->prefix[RIPNG_FILTER_IN] = NULL;
+
+ if (dist->prefix[DISTRIBUTE_V6_OUT]) {
+ plist = prefix_list_lookup(AFI_IP6,
+ dist->prefix[DISTRIBUTE_V6_OUT]);
+ if (plist)
+ ri->prefix[RIPNG_FILTER_OUT] = plist;
+ else
+ ri->prefix[RIPNG_FILTER_OUT] = NULL;
+ } else
+ ri->prefix[RIPNG_FILTER_OUT] = NULL;
+}
+
+void ripng_distribute_update_interface(struct interface *ifp)
+{
+ struct ripng_interface *ri = ifp->info;
+ struct ripng *ripng = ri->ripng;
+ struct distribute *dist;
+
+ if (!ripng)
+ return;
+ dist = distribute_lookup(ripng->distribute_ctx, ifp->name);
+ if (dist)
+ ripng_distribute_update(ripng->distribute_ctx, dist);
+}
+
+/* Update all interface's distribute list. */
+static void ripng_distribute_update_all(struct prefix_list *notused)
+{
+ struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct interface *ifp;
+
+ FOR_ALL_INTERFACES (vrf, ifp)
+ ripng_distribute_update_interface(ifp);
+}
+
+static void ripng_distribute_update_all_wrapper(struct access_list *notused)
+{
+ ripng_distribute_update_all(NULL);
+}
+
+/* delete all the added ripng routes. */
+void ripng_clean(struct ripng *ripng)
+{
+ ripng_interface_clean(ripng);
+
+ if (ripng->enabled)
+ ripng_instance_disable(ripng);
+
+ for (int i = 0; i < ZEBRA_ROUTE_MAX; i++)
+ if (ripng->redist[i].route_map.name)
+ free(ripng->redist[i].route_map.name);
+
+ agg_table_finish(ripng->table);
+ list_delete(&ripng->peer_list);
+ distribute_list_delete(&ripng->distribute_ctx);
+ if_rmap_ctx_delete(ripng->if_rmap_ctx);
+
+ stream_free(ripng->ibuf);
+ stream_free(ripng->obuf);
+
+ ripng_clean_network(ripng);
+ ripng_passive_interface_clean(ripng);
+ vector_free(ripng->enable_if);
+ agg_table_finish(ripng->enable_network);
+ vector_free(ripng->passive_interface);
+ list_delete(&ripng->offset_list_master);
+
+ RB_REMOVE(ripng_instance_head, &ripng_instances, ripng);
+ XFREE(MTYPE_RIPNG_VRF_NAME, ripng->vrf_name);
+ XFREE(MTYPE_RIPNG, ripng);
+}
+
+static void ripng_if_rmap_update(struct if_rmap_ctx *ctx,
+ struct if_rmap *if_rmap)
+{
+ struct interface *ifp = NULL;
+ struct ripng_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[RIPNG_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[RIPNG_FILTER_OUT] = NULL;
+}
+
+void ripng_if_rmap_update_interface(struct interface *ifp)
+{
+ struct ripng_interface *ri = ifp->info;
+ struct ripng *ripng = ri->ripng;
+ struct if_rmap *if_rmap;
+ struct if_rmap_ctx *ctx;
+
+ if (!ripng)
+ return;
+ ctx = ripng->if_rmap_ctx;
+ if (!ctx)
+ return;
+ if_rmap = if_rmap_lookup(ctx, ifp->name);
+ if (if_rmap)
+ ripng_if_rmap_update(ctx, if_rmap);
+}
+
+static void ripng_routemap_update_redistribute(struct ripng *ripng)
+{
+ for (int i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+ if (ripng->redist[i].route_map.name) {
+ ripng->redist[i].route_map.map =
+ route_map_lookup_by_name(
+ ripng->redist[i].route_map.name);
+ route_map_counter_increment(
+ ripng->redist[i].route_map.map);
+ }
+ }
+}
+
+static void ripng_routemap_update(const char *unused)
+{
+ struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct ripng *ripng;
+ struct interface *ifp;
+
+ FOR_ALL_INTERFACES (vrf, ifp)
+ ripng_if_rmap_update_interface(ifp);
+
+ ripng = vrf->info;
+ if (ripng)
+ ripng_routemap_update_redistribute(ripng);
+}
+
+/* Link RIPng instance to VRF. */
+static void ripng_vrf_link(struct ripng *ripng, struct vrf *vrf)
+{
+ struct interface *ifp;
+
+ ripng->vrf = vrf;
+ ripng->distribute_ctx->vrf = vrf;
+ vrf->info = ripng;
+
+ FOR_ALL_INTERFACES (vrf, ifp)
+ ripng_interface_sync(ifp);
+}
+
+/* Unlink RIPng instance from VRF. */
+static void ripng_vrf_unlink(struct ripng *ripng, struct vrf *vrf)
+{
+ struct interface *ifp;
+
+ ripng->vrf = NULL;
+ ripng->distribute_ctx->vrf = NULL;
+ vrf->info = NULL;
+
+ FOR_ALL_INTERFACES (vrf, ifp)
+ ripng_interface_sync(ifp);
+}
+
+static void ripng_instance_enable(struct ripng *ripng, struct vrf *vrf,
+ int sock)
+{
+ ripng->sock = sock;
+
+ ripng_vrf_link(ripng, vrf);
+ ripng->enabled = true;
+
+ /* Resend all redistribute requests. */
+ ripng_redistribute_enable(ripng);
+
+ /* Create read and timer thread. */
+ ripng_event(ripng, RIPNG_READ, ripng->sock);
+ ripng_event(ripng, RIPNG_UPDATE_EVENT, 1);
+
+ ripng_zebra_vrf_register(vrf);
+}
+
+static void ripng_instance_disable(struct ripng *ripng)
+{
+ struct vrf *vrf = ripng->vrf;
+ struct agg_node *rp;
+
+ /* Clear RIPng routes */
+ for (rp = agg_route_top(ripng->table); rp; rp = agg_route_next(rp)) {
+ struct ripng_aggregate *aggregate;
+ struct list *list;
+
+ if ((list = rp->info) != NULL) {
+ struct ripng_info *rinfo;
+ struct listnode *listnode;
+
+ rinfo = listgetdata(listhead(list));
+ if (ripng_route_rte(rinfo))
+ ripng_zebra_ipv6_delete(ripng, rp);
+
+ for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) {
+ EVENT_OFF(rinfo->t_timeout);
+ EVENT_OFF(rinfo->t_garbage_collect);
+ ripng_info_free(rinfo);
+ }
+ list_delete(&list);
+ rp->info = NULL;
+ agg_unlock_node(rp);
+ }
+
+ if ((aggregate = rp->aggregate) != NULL) {
+ ripng_aggregate_free(aggregate);
+ rp->aggregate = NULL;
+ agg_unlock_node(rp);
+ }
+ }
+
+ /* Flush all redistribute requests. */
+ ripng_redistribute_disable(ripng);
+
+ /* Cancel the RIPng timers */
+ EVENT_OFF(ripng->t_update);
+ EVENT_OFF(ripng->t_triggered_update);
+ EVENT_OFF(ripng->t_triggered_interval);
+
+ /* Cancel the read thread */
+ EVENT_OFF(ripng->t_read);
+
+ /* Close the RIPng socket */
+ if (ripng->sock >= 0) {
+ close(ripng->sock);
+ ripng->sock = -1;
+ }
+
+ /* Clear existing peers. */
+ list_delete_all_node(ripng->peer_list);
+
+ ripng_zebra_vrf_deregister(vrf);
+
+ ripng_vrf_unlink(ripng, vrf);
+ ripng->enabled = false;
+}
+
+static int ripng_vrf_new(struct vrf *vrf)
+{
+ if (IS_RIPNG_DEBUG_EVENT)
+ zlog_debug("%s: VRF created: %s(%u)", __func__, vrf->name,
+ vrf->vrf_id);
+
+ return 0;
+}
+
+static int ripng_vrf_delete(struct vrf *vrf)
+{
+ if (IS_RIPNG_DEBUG_EVENT)
+ zlog_debug("%s: VRF deleted: %s(%u)", __func__, vrf->name,
+ vrf->vrf_id);
+
+ return 0;
+}
+
+static int ripng_vrf_enable(struct vrf *vrf)
+{
+ struct ripng *ripng;
+ int socket;
+
+ ripng = ripng_lookup_by_vrf_name(vrf->name);
+ if (!ripng || ripng->enabled)
+ return 0;
+
+ if (IS_RIPNG_DEBUG_EVENT)
+ zlog_debug("%s: VRF %s(%u) enabled", __func__, vrf->name,
+ vrf->vrf_id);
+
+ /* Activate the VRF RIPng instance. */
+ socket = ripng_make_socket(vrf);
+ if (socket < 0)
+ return -1;
+
+ ripng_instance_enable(ripng, vrf, socket);
+
+ return 0;
+}
+
+static int ripng_vrf_disable(struct vrf *vrf)
+{
+ struct ripng *ripng;
+
+ ripng = ripng_lookup_by_vrf_name(vrf->name);
+ if (!ripng || !ripng->enabled)
+ return 0;
+
+ if (IS_RIPNG_DEBUG_EVENT)
+ zlog_debug("%s: VRF %s(%u) disabled", __func__, vrf->name,
+ vrf->vrf_id);
+
+ /* Deactivate the VRF RIPng instance. */
+ if (ripng->enabled)
+ ripng_instance_disable(ripng);
+
+ return 0;
+}
+
+void ripng_vrf_init(void)
+{
+ vrf_init(ripng_vrf_new, ripng_vrf_enable, ripng_vrf_disable,
+ ripng_vrf_delete);
+
+ vrf_cmd_init(NULL);
+}
+
+void ripng_vrf_terminate(void)
+{
+ vrf_terminate();
+}
+
+/* Initialize ripng structure and set commands. */
+void ripng_init(void)
+{
+ /* Install RIPNG_NODE. */
+ install_node(&cmd_ripng_node);
+
+ /* Install ripng commands. */
+ install_element(VIEW_NODE, &show_ipv6_ripng_cmd);
+ install_element(VIEW_NODE, &show_ipv6_ripng_status_cmd);
+
+ install_default(RIPNG_NODE);
+
+ ripng_if_init();
+ ripng_debug_init();
+
+ /* Access list install. */
+ access_list_init();
+ access_list_add_hook(ripng_distribute_update_all_wrapper);
+ access_list_delete_hook(ripng_distribute_update_all_wrapper);
+
+ /* Prefix list initialize.*/
+ prefix_list_init();
+ prefix_list_add_hook(ripng_distribute_update_all);
+ prefix_list_delete_hook(ripng_distribute_update_all);
+
+ /* Route-map for interface. */
+ ripng_route_map_init();
+
+ route_map_add_hook(ripng_routemap_update);
+ route_map_delete_hook(ripng_routemap_update);
+
+ if_rmap_init(RIPNG_NODE);
+}
diff --git a/ripngd/ripngd.h b/ripngd/ripngd.h
new file mode 100644
index 0000000..3a2bc0c
--- /dev/null
+++ b/ripngd/ripngd.h
@@ -0,0 +1,439 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RIPng related value and structure.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ */
+
+#ifndef _ZEBRA_RIPNG_RIPNGD_H
+#define _ZEBRA_RIPNG_RIPNGD_H
+
+#include <zclient.h>
+#include <vty.h>
+#include <distribute.h>
+#include <vector.h>
+#include <memory.h>
+
+/* RIPng version and port number. */
+#define RIPNG_V1 1
+#define RIPNG_PORT_DEFAULT 521
+#define RIPNG_VTY_PORT 2603
+#define RIPNG_MAX_PACKET_SIZE 1500
+#define RIPNG_PRIORITY_DEFAULT 0
+
+/* RIPng commands. */
+#define RIPNG_REQUEST 1
+#define RIPNG_RESPONSE 2
+
+/* RIPng metric and multicast group address. */
+#define RIPNG_METRIC_INFINITY 16
+#define RIPNG_METRIC_NEXTHOP 0xff
+#define RIPNG_GROUP "ff02::9"
+
+/* RIPng peer timeout value. */
+#define RIPNG_PEER_TIMER_DEFAULT 180
+
+/* Default config file name. */
+#define RIPNG_DEFAULT_CONFIG "ripngd.conf"
+
+/* RIPng route types. */
+#define RIPNG_ROUTE_RTE 0
+#define RIPNG_ROUTE_STATIC 1
+#define RIPNG_ROUTE_DEFAULT 2
+#define RIPNG_ROUTE_REDISTRIBUTE 3
+#define RIPNG_ROUTE_INTERFACE 4
+#define RIPNG_ROUTE_AGGREGATE 5
+
+/* Interface send/receive configuration. */
+#define RIPNG_SEND_UNSPEC 0
+#define RIPNG_SEND_OFF 1
+#define RIPNG_RECEIVE_UNSPEC 0
+#define RIPNG_RECEIVE_OFF 1
+
+/* RIP default route's accept/announce methods. */
+#define RIPNG_DEFAULT_ADVERTISE_UNSPEC 0
+#define RIPNG_DEFAULT_ADVERTISE_NONE 1
+#define RIPNG_DEFAULT_ADVERTISE 2
+
+#define RIPNG_DEFAULT_ACCEPT_UNSPEC 0
+#define RIPNG_DEFAULT_ACCEPT_NONE 1
+#define RIPNG_DEFAULT_ACCEPT 2
+
+/* For max RTE calculation. */
+#ifndef IPV6_HDRLEN
+#define IPV6_HDRLEN 40
+#endif /* IPV6_HDRLEN */
+
+#ifndef IFMINMTU
+#define IFMINMTU 576
+#endif /* IFMINMTU */
+
+/* YANG paths */
+#define RIPNG_INSTANCE "/frr-ripngd:ripngd/instance"
+#define RIPNG_IFACE "/frr-interface:lib/interface/frr-ripngd:ripng"
+
+DECLARE_MGROUP(RIPNGD);
+
+/* RIPng structure. */
+struct ripng {
+ RB_ENTRY(ripng) 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;
+
+ /* RIPng socket. */
+ int sock;
+
+ /* RIPng Parameters.*/
+ uint8_t command;
+ uint8_t version;
+ uint16_t update_time;
+ uint16_t timeout_time;
+ uint16_t garbage_time;
+ int max_mtu;
+ uint8_t default_metric;
+
+ /* Input/output buffer of RIPng. */
+ struct stream *ibuf;
+ struct stream *obuf;
+
+ /* RIPng routing information base. */
+ struct agg_table *table;
+
+ /* Linked list of RIPng peers. */
+ struct list *peer_list;
+
+ /* RIPng enabled interfaces. */
+ vector enable_if;
+
+ /* RIPng enabled networks. */
+ struct agg_table *enable_network;
+
+ /* Vector to store passive-interface name. */
+ vector passive_interface;
+
+ /* RIPng offset-lists. */
+ struct list *offset_list_master;
+
+ /* RIPng threads. */
+ struct event *t_read;
+ struct event *t_update;
+
+ /* Triggered update hack. */
+ int trigger;
+ struct event *t_triggered_update;
+ struct event *t_triggered_interval;
+
+ /* RIPng ECMP flag */
+ uint8_t ecmp;
+
+ /* RIPng 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;
+};
+RB_HEAD(ripng_instance_head, ripng);
+RB_PROTOTYPE(ripng_instance_head, ripng, entry, ripng_instance_compare)
+
+/* Routing table entry. */
+struct rte {
+ struct in6_addr addr; /* RIPng destination prefix */
+ uint16_t tag; /* RIPng tag */
+ uint8_t prefixlen; /* Length of the RIPng prefix */
+ uint8_t metric; /* Metric of the RIPng route */
+ /* The nexthop is stored by the structure
+ * ripng_nexthop within ripngd.c */
+};
+
+/* RIPNG send packet. */
+struct ripng_packet {
+ uint8_t command;
+ uint8_t version;
+ uint16_t zero;
+ struct rte rte[1];
+};
+
+/* Each route's information. */
+struct ripng_info {
+ /* This route's type. Static, ripng or aggregate. */
+ uint8_t type;
+
+ /* Sub type for static route. */
+ uint8_t sub_type;
+
+ /* RIPng specific information */
+ struct in6_addr nexthop;
+ struct in6_addr from;
+
+ /* Which interface does this route come from. */
+ ifindex_t ifindex;
+
+ /* Metric of this route. */
+ uint8_t metric;
+
+ /* Tag field of RIPng packet.*/
+ uint16_t tag;
+
+ /* For aggregation. */
+ unsigned int suppress;
+
+/* Flags of RIPng route. */
+#define RIPNG_RTF_FIB 1
+#define RIPNG_RTF_CHANGED 2
+ uint8_t flags;
+
+ /* Garbage collect timer. */
+ struct event *t_timeout;
+ struct event *t_garbage_collect;
+
+ /* Route-map features - this variables can be changed. */
+ struct in6_addr nexthop_out;
+ uint8_t metric_set;
+ uint8_t metric_out;
+ uint16_t tag_out;
+
+ struct agg_node *rp;
+};
+
+typedef enum {
+ RIPNG_NO_SPLIT_HORIZON = 0,
+ RIPNG_SPLIT_HORIZON,
+ RIPNG_SPLIT_HORIZON_POISONED_REVERSE
+} split_horizon_policy_t;
+
+/* RIPng specific interface configuration. */
+struct ripng_interface {
+ /* Parent routing instance. */
+ struct ripng *ripng;
+
+ /* RIPng is enabled on this interface. */
+ int enable_network;
+ int enable_interface;
+
+ /* RIPng is running on this interface. */
+ int running;
+
+ /* Split horizon flag. */
+ split_horizon_policy_t split_horizon;
+
+/* For filter type slot. */
+#define RIPNG_FILTER_IN 0
+#define RIPNG_FILTER_OUT 1
+#define RIPNG_FILTER_MAX 2
+
+ /* Access-list. */
+ struct access_list *list[RIPNG_FILTER_MAX];
+
+ /* Prefix-list. */
+ struct prefix_list *prefix[RIPNG_FILTER_MAX];
+
+ /* Route-map. */
+ struct route_map *routemap[RIPNG_FILTER_MAX];
+
+ /* Default information originate. */
+ uint8_t default_originate;
+
+ /* Default information only. */
+ uint8_t default_only;
+
+ /* Wake up thread. */
+ struct event *t_wakeup;
+
+ /* Passive interface. */
+ int passive;
+};
+
+/* RIPng peer information. */
+struct ripng_peer {
+ /* Parent routing instance. */
+ struct ripng *ripng;
+
+ /* Peer address. */
+ struct in6_addr addr;
+
+ /* Peer RIPng 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;
+};
+
+/* All RIPng events. */
+enum ripng_event {
+ RIPNG_READ,
+ RIPNG_ZEBRA,
+ RIPNG_REQUEST_EVENT,
+ RIPNG_UPDATE_EVENT,
+ RIPNG_TRIGGERED_UPDATE,
+};
+
+/* RIPng timer on/off macro. */
+#define RIPNG_TIMER_ON(T, F, V) event_add_timer(master, (F), rinfo, (V), &(T))
+
+#define RIPNG_OFFSET_LIST_IN 0
+#define RIPNG_OFFSET_LIST_OUT 1
+#define RIPNG_OFFSET_LIST_MAX 2
+
+struct ripng_offset_list {
+ /* Parent routing instance. */
+ struct ripng *ripng;
+
+ char *ifname;
+
+ struct {
+ char *alist_name;
+ /* struct access_list *alist; */
+ uint8_t metric;
+ } direct[RIPNG_OFFSET_LIST_MAX];
+};
+
+/* Extern variables. */
+extern struct zebra_privs_t ripngd_privs;
+extern struct event_loop *master;
+extern struct ripng_instance_head ripng_instances;
+
+/* Prototypes. */
+extern void ripng_init(void);
+extern void ripng_clean(struct ripng *ripng);
+extern void ripng_clean_network(struct ripng *ripng);
+extern void ripng_interface_clean(struct ripng *ripng);
+extern int ripng_enable_network_add(struct ripng *ripng, struct prefix *p);
+extern int ripng_enable_network_delete(struct ripng *ripng, struct prefix *p);
+extern int ripng_enable_if_add(struct ripng *ripng, const char *ifname);
+extern int ripng_enable_if_delete(struct ripng *ripng, const char *ifname);
+extern int ripng_passive_interface_set(struct ripng *ripng, const char *ifname);
+extern int ripng_passive_interface_unset(struct ripng *ripng,
+ const char *ifname);
+extern void ripng_passive_interface_clean(struct ripng *ripng);
+extern void ripng_if_init(void);
+extern void ripng_route_map_init(void);
+extern void ripng_zebra_vrf_register(struct vrf *vrf);
+extern void ripng_zebra_vrf_deregister(struct vrf *vrf);
+extern void ripng_terminate(void);
+/* zclient_init() is done by ripng_zebra.c:zebra_init() */
+extern void zebra_init(struct event_loop *master);
+extern void ripng_zebra_stop(void);
+extern void ripng_redistribute_conf_update(struct ripng *ripng, int type);
+extern void ripng_redistribute_conf_delete(struct ripng *ripng, int type);
+
+extern void ripng_peer_update(struct ripng *ripng, struct sockaddr_in6 *from,
+ uint8_t version);
+extern void ripng_peer_bad_route(struct ripng *ripng,
+ struct sockaddr_in6 *from);
+extern void ripng_peer_bad_packet(struct ripng *ripng,
+ struct sockaddr_in6 *from);
+extern void ripng_peer_display(struct vty *vty, struct ripng *ripng);
+extern struct ripng_peer *ripng_peer_lookup(struct ripng *ripng,
+ struct in6_addr *addr);
+extern struct ripng_peer *ripng_peer_lookup_next(struct ripng *ripng,
+ struct in6_addr *addr);
+extern int ripng_peer_list_cmp(struct ripng_peer *p1, struct ripng_peer *p2);
+extern void ripng_peer_list_del(void *arg);
+
+extern struct ripng_offset_list *ripng_offset_list_new(struct ripng *ripng,
+ const char *ifname);
+extern void ripng_offset_list_del(struct ripng_offset_list *offset);
+extern void ripng_offset_list_free(struct ripng_offset_list *offset);
+extern struct ripng_offset_list *ripng_offset_list_lookup(struct ripng *ripng,
+ const char *ifname);
+extern int ripng_offset_list_apply_in(struct ripng *ripng,
+ struct prefix_ipv6 *p,
+ struct interface *ifp, uint8_t *metric);
+extern int ripng_offset_list_apply_out(struct ripng *ripng,
+ struct prefix_ipv6 *p,
+ struct interface *ifp, uint8_t *metric);
+extern int offset_list_cmp(struct ripng_offset_list *o1,
+ struct ripng_offset_list *o2);
+
+extern int ripng_route_rte(struct ripng_info *rinfo);
+extern struct ripng_info *ripng_info_new(void);
+extern void ripng_info_free(struct ripng_info *rinfo);
+extern struct ripng *ripng_info_get_instance(const struct ripng_info *rinfo);
+extern void ripng_event(struct ripng *ripng, enum ripng_event event, int sock);
+extern int ripng_request(struct interface *ifp);
+extern void ripng_redistribute_add(struct ripng *ripng, int type, int sub_type,
+ struct prefix_ipv6 *p, ifindex_t ifindex,
+ struct in6_addr *nexthop, route_tag_t tag);
+extern void ripng_redistribute_delete(struct ripng *ripng, int type,
+ int sub_type, struct prefix_ipv6 *p,
+ ifindex_t ifindex);
+extern void ripng_redistribute_withdraw(struct ripng *ripng, int type);
+
+extern void ripng_ecmp_disable(struct ripng *ripng);
+extern void ripng_distribute_update_interface(struct interface *);
+extern void ripng_if_rmap_update_interface(struct interface *);
+
+extern void ripng_zebra_ipv6_add(struct ripng *ripng, struct agg_node *node);
+extern void ripng_zebra_ipv6_delete(struct ripng *ripng, struct agg_node *node);
+
+extern void ripng_redistribute_enable(struct ripng *ripng);
+extern void ripng_redistribute_disable(struct ripng *ripng);
+extern int ripng_redistribute_check(struct ripng *ripng, int type);
+extern void ripng_redistribute_write(struct vty *vty, struct ripng *ripng);
+
+extern int ripng_write_rte(int num, struct stream *s, struct prefix_ipv6 *p,
+ struct in6_addr *nexthop, uint16_t tag,
+ uint8_t metric);
+extern int ripng_send_packet(caddr_t buf, int bufsize, struct sockaddr_in6 *to,
+ struct interface *ifp);
+
+extern void ripng_packet_dump(struct ripng_packet *packet, int size,
+ const char *sndrcv);
+
+extern int ripng_interface_up(ZAPI_CALLBACK_ARGS);
+extern int ripng_interface_down(ZAPI_CALLBACK_ARGS);
+extern int ripng_interface_add(ZAPI_CALLBACK_ARGS);
+extern int ripng_interface_delete(ZAPI_CALLBACK_ARGS);
+extern int ripng_interface_address_add(ZAPI_CALLBACK_ARGS);
+extern int ripng_interface_address_delete(ZAPI_CALLBACK_ARGS);
+extern void ripng_interface_sync(struct interface *ifp);
+
+extern struct ripng *ripng_lookup_by_vrf_id(vrf_id_t vrf_id);
+extern struct ripng *ripng_lookup_by_vrf_name(const char *vrf_name);
+extern struct ripng *ripng_create(const char *vrf_name, struct vrf *vrf,
+ int socket);
+extern int ripng_make_socket(struct vrf *vrf);
+extern int ripng_network_write(struct vty *vty, struct ripng *ripng);
+
+extern struct ripng_info *ripng_ecmp_add(struct ripng *ripng,
+ struct ripng_info *rinfo);
+extern struct ripng_info *ripng_ecmp_replace(struct ripng *ripng,
+ struct ripng_info *rinfo);
+extern struct ripng_info *ripng_ecmp_delete(struct ripng *ripng,
+ struct ripng_info *rinfo);
+extern void ripng_ecmp_change(struct ripng *ripng);
+
+extern void ripng_vrf_init(void);
+extern void ripng_vrf_terminate(void);
+extern void ripng_cli_init(void);
+
+extern uint32_t zebra_ecmp_count;
+
+#endif /* _ZEBRA_RIPNG_RIPNGD_H */
diff --git a/ripngd/subdir.am b/ripngd/subdir.am
new file mode 100644
index 0000000..162426c
--- /dev/null
+++ b/ripngd/subdir.am
@@ -0,0 +1,44 @@
+#
+# ripngd
+#
+
+if RIPNGD
+sbin_PROGRAMS += ripngd/ripngd
+vtysh_daemons += ripngd
+man8 += $(MANBUILD)/frr-ripngd.8
+endif
+
+ripngd_ripngd_SOURCES = \
+ ripngd/ripng_cli.c \
+ ripngd/ripng_debug.c \
+ ripngd/ripng_interface.c \
+ ripngd/ripng_nexthop.c \
+ ripngd/ripng_offset.c \
+ ripngd/ripng_main.c \
+ ripngd/ripng_nb.c \
+ ripngd/ripng_nb_config.c \
+ ripngd/ripng_nb_rpcs.c \
+ ripngd/ripng_nb_state.c \
+ ripngd/ripng_peer.c \
+ ripngd/ripng_route.c \
+ ripngd/ripng_routemap.c \
+ ripngd/ripng_zebra.c \
+ ripngd/ripngd.c \
+ # end
+
+clippy_scan += \
+ ripngd/ripng_cli.c \
+ # end
+
+noinst_HEADERS += \
+ ripngd/ripng_debug.h \
+ ripngd/ripng_nb.h \
+ ripngd/ripng_nexthop.h \
+ ripngd/ripng_route.h \
+ ripngd/ripngd.h \
+ # end
+
+ripngd_ripngd_LDADD = lib/libfrr.la $(LIBCAP)
+nodist_ripngd_ripngd_SOURCES = \
+ yang/frr-ripngd.yang.c \
+ # end