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