From e2bbf175a2184bd76f6c54ccf8456babeb1a46fc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Tue, 9 Apr 2024 15:16:35 +0200 Subject: Adding upstream version 9.1. Signed-off-by: Daniel Baumann --- ripngd/.gitignore | 2 + ripngd/Makefile | 10 + ripngd/ripng_cli.c | 589 ++++++++++ ripngd/ripng_debug.c | 227 ++++ ripngd/ripng_debug.h | 34 + ripngd/ripng_interface.c | 882 +++++++++++++++ ripngd/ripng_main.c | 171 +++ ripngd/ripng_nb.c | 262 +++++ ripngd/ripng_nb.h | 137 +++ ripngd/ripng_nb_config.c | 754 +++++++++++++ ripngd/ripng_nb_rpcs.c | 94 ++ ripngd/ripng_nb_state.c | 220 ++++ ripngd/ripng_nexthop.c | 209 ++++ ripngd/ripng_nexthop.h | 49 + ripngd/ripng_offset.c | 149 +++ ripngd/ripng_peer.c | 164 +++ ripngd/ripng_route.c | 152 +++ ripngd/ripng_route.h | 40 + ripngd/ripng_routemap.c | 423 ++++++++ ripngd/ripng_zebra.c | 250 +++++ ripngd/ripngd.c | 2718 ++++++++++++++++++++++++++++++++++++++++++++++ ripngd/ripngd.h | 439 ++++++++ ripngd/subdir.am | 44 + 23 files changed, 8019 insertions(+) create mode 100644 ripngd/.gitignore create mode 100644 ripngd/Makefile create mode 100644 ripngd/ripng_cli.c create mode 100644 ripngd/ripng_debug.c create mode 100644 ripngd/ripng_debug.h create mode 100644 ripngd/ripng_interface.c create mode 100644 ripngd/ripng_main.c create mode 100644 ripngd/ripng_nb.c create mode 100644 ripngd/ripng_nb.h create mode 100644 ripngd/ripng_nb_config.c create mode 100644 ripngd/ripng_nb_rpcs.c create mode 100644 ripngd/ripng_nb_state.c create mode 100644 ripngd/ripng_nexthop.c create mode 100644 ripngd/ripng_nexthop.h create mode 100644 ripngd/ripng_offset.c create mode 100644 ripngd/ripng_peer.c create mode 100644 ripngd/ripng_route.c create mode 100644 ripngd/ripng_route.h create mode 100644 ripngd/ripng_routemap.c create mode 100644 ripngd/ripng_zebra.c create mode 100644 ripngd/ripngd.c create mode 100644 ripngd/ripngd.h create mode 100644 ripngd/subdir.am (limited to 'ripngd') 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 + +#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 $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 [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 [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 +#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 ", + 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 ", + 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 + +#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 + +#include +#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 + +#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 + +#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 + +#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 + +#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 + */ + +/* This file is required in order to support properly the RIPng nexthop + * feature. + */ + +#include + +/* For struct udphdr. */ +#include + +#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 + */ + +#ifndef _ZEBRA_RIPNG_RIPNG_NEXTHOP_H +#define _ZEBRA_RIPNG_RIPNG_NEXTHOP_H + +#include +#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 + */ + +/* RIPng support by Vincent Jardin + * Copyright (C) 2002 6WIND + */ + +#include + +#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 + */ + +/* RIPng support added by Vincent Jardin + * Copyright (C) 2002 6WIND + */ + +#include + +#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 + +#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 + +#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 + +#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 + +#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 +#include +#include +#include +#include + +/* 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 -- cgit v1.2.3