diff options
Diffstat (limited to 'eigrpd')
52 files changed, 15549 insertions, 0 deletions
diff --git a/eigrpd/.gitignore b/eigrpd/.gitignore new file mode 100644 index 0000000..0303c6f --- /dev/null +++ b/eigrpd/.gitignore @@ -0,0 +1,2 @@ +eigrpd +eigrpd.conf diff --git a/eigrpd/Makefile b/eigrpd/Makefile new file mode 100644 index 0000000..b6d6076 --- /dev/null +++ b/eigrpd/Makefile @@ -0,0 +1,10 @@ +all: ALWAYS + @$(MAKE) -s -C .. eigrpd/eigrpd +%: ALWAYS + @$(MAKE) -s -C .. eigrpd/$@ + +Makefile: + #nothing +ALWAYS: +.PHONY: ALWAYS makefiles +.SUFFIXES: diff --git a/eigrpd/eigrp_cli.c b/eigrpd/eigrp_cli.c new file mode 100644 index 0000000..213834a --- /dev/null +++ b/eigrpd/eigrp_cli.c @@ -0,0 +1,898 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP daemon CLI implementation. + * + * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF") + * Rafael Zalamena + */ + +#include <zebra.h> + +#include "lib/command.h" +#include "lib/log.h" +#include "lib/northbound_cli.h" + +#include "eigrp_structs.h" +#include "eigrpd.h" +#include "eigrp_zebra.h" +#include "eigrp_cli.h" + +#include "eigrpd/eigrp_cli_clippy.c" + +/* + * XPath: /frr-eigrpd:eigrpd/instance + */ +DEFPY_YANG_NOSH( + router_eigrp, + router_eigrp_cmd, + "router eigrp (1-65535)$as [vrf NAME]", + ROUTER_STR + EIGRP_STR + AS_STR + VRF_CMD_HELP_STR) +{ + char xpath[XPATH_MAXLEN]; + int rv; + + snprintf(xpath, sizeof(xpath), + "/frr-eigrpd:eigrpd/instance[asn='%s'][vrf='%s']", + as_str, vrf ? vrf : VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + VTY_PUSH_XPATH(EIGRP_NODE, xpath); + + return rv; +} + +DEFPY_YANG( + no_router_eigrp, + no_router_eigrp_cmd, + "no router eigrp (1-65535)$as [vrf NAME]", + NO_STR + ROUTER_STR + EIGRP_STR + AS_STR + VRF_CMD_HELP_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-eigrpd:eigrpd/instance[asn='%s'][vrf='%s']", + as_str, vrf ? vrf : VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes_clear_pending(vty, NULL); +} + +void eigrp_cli_show_header(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + const char *asn = yang_dnode_get_string(dnode, "./asn"); + const char *vrf = yang_dnode_get_string(dnode, "./vrf"); + + vty_out(vty, "router eigrp %s", asn); + if (strcmp(vrf, VRF_DEFAULT_NAME)) + vty_out(vty, " vrf %s", vrf); + vty_out(vty, "\n"); +} + +void eigrp_cli_show_end_header(struct vty *vty, const struct lyd_node *dnode) +{ + vty_out(vty, "exit\n"); + vty_out(vty, "!\n"); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/router-id + */ +DEFPY_YANG( + eigrp_router_id, + eigrp_router_id_cmd, + "eigrp router-id A.B.C.D$addr", + EIGRP_STR + "Router ID for this EIGRP process\n" + "EIGRP Router-ID in IP address format\n") +{ + nb_cli_enqueue_change(vty, "./router-id", NB_OP_MODIFY, addr_str); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_router_id, + no_eigrp_router_id_cmd, + "no eigrp router-id [A.B.C.D]", + NO_STR + EIGRP_STR + "Router ID for this EIGRP process\n" + "EIGRP Router-ID in IP address format\n") +{ + nb_cli_enqueue_change(vty, "./router-id", NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_router_id(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + const char *router_id = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " eigrp router-id %s\n", router_id); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/passive-interface + */ +DEFPY_YANG( + eigrp_passive_interface, + eigrp_passive_interface_cmd, + "[no] passive-interface IFNAME", + NO_STR + "Suppress routing updates on an interface\n" + "Interface to suppress on\n") +{ + if (no) + nb_cli_enqueue_change(vty, "./passive-interface", + NB_OP_DESTROY, ifname); + else + nb_cli_enqueue_change(vty, "./passive-interface", + NB_OP_CREATE, ifname); + + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_passive_interface(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + const char *ifname = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " passive-interface %s\n", ifname); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/active-time + */ +DEFPY_YANG( + eigrp_timers_active, + eigrp_timers_active_cmd, + "timers active-time <(1-65535)$timer|disabled$disabled>", + "Adjust routing timers\n" + "Time limit for active state\n" + "Active state time limit in seconds\n" + "Disable time limit for active state\n") +{ + if (disabled) + nb_cli_enqueue_change(vty, "./active-time", NB_OP_MODIFY, "0"); + else + nb_cli_enqueue_change(vty, "./active-time", + NB_OP_MODIFY, timer_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_timers_active, + no_eigrp_timers_active_cmd, + "no timers active-time [<(1-65535)|disabled>]", + NO_STR + "Adjust routing timers\n" + "Time limit for active state\n" + "Active state time limit in seconds\n" + "Disable time limit for active state\n") +{ + nb_cli_enqueue_change(vty, "./active-time", NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_active_time(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + const char *timer = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " timers active-time %s\n", timer); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/variance + */ +DEFPY_YANG( + eigrp_variance, + eigrp_variance_cmd, + "variance (1-128)$variance", + "Control load balancing variance\n" + "Metric variance multiplier\n") +{ + nb_cli_enqueue_change(vty, "./variance", NB_OP_MODIFY, variance_str); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_variance, + no_eigrp_variance_cmd, + "no variance [(1-128)]", + NO_STR + "Control load balancing variance\n" + "Metric variance multiplier\n") +{ + nb_cli_enqueue_change(vty, "./variance", NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_variance(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + const char *variance = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " variance %s\n", variance); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/maximum-paths + */ +DEFPY_YANG( + eigrp_maximum_paths, + eigrp_maximum_paths_cmd, + "maximum-paths (1-32)$maximum_paths", + "Forward packets over multiple paths\n" + "Number of paths\n") +{ + nb_cli_enqueue_change(vty, "./maximum-paths", NB_OP_MODIFY, + maximum_paths_str); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_maximum_paths, + no_eigrp_maximum_paths_cmd, + "no maximum-paths [(1-32)]", + NO_STR + "Forward packets over multiple paths\n" + "Number of paths\n") +{ + nb_cli_enqueue_change(vty, "./maximum-paths", NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_maximum_paths(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + const char *maximum_paths = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " maximum-paths %s\n", maximum_paths); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K1 + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K2 + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K3 + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K4 + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K5 + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K6 + */ +DEFPY_YANG( + eigrp_metric_weights, + eigrp_metric_weights_cmd, + "metric weights (0-255)$k1 (0-255)$k2 (0-255)$k3 (0-255)$k4 (0-255)$k5 [(0-255)$k6]", + "Modify metrics and parameters for advertisement\n" + "Modify metric coefficients\n" + "K1\n" + "K2\n" + "K3\n" + "K4\n" + "K5\n" + "K6\n") +{ + nb_cli_enqueue_change(vty, "./metric-weights/K1", NB_OP_MODIFY, k1_str); + nb_cli_enqueue_change(vty, "./metric-weights/K2", NB_OP_MODIFY, k2_str); + nb_cli_enqueue_change(vty, "./metric-weights/K3", NB_OP_MODIFY, k3_str); + nb_cli_enqueue_change(vty, "./metric-weights/K4", NB_OP_MODIFY, k4_str); + nb_cli_enqueue_change(vty, "./metric-weights/K5", NB_OP_MODIFY, k5_str); + if (k6) + nb_cli_enqueue_change(vty, "./metric-weights/K6", + NB_OP_MODIFY, k6_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_metric_weights, + no_eigrp_metric_weights_cmd, + "no metric weights [(0-255) (0-255) (0-255) (0-255) (0-255) (0-255)]", + NO_STR + "Modify metrics and parameters for advertisement\n" + "Modify metric coefficients\n" + "K1\n" + "K2\n" + "K3\n" + "K4\n" + "K5\n" + "K6\n") +{ + nb_cli_enqueue_change(vty, "./metric-weights/K1", NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, "./metric-weights/K2", NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, "./metric-weights/K3", NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, "./metric-weights/K4", NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, "./metric-weights/K5", NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, "./metric-weights/K6", NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_metrics(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + const char *k1, *k2, *k3, *k4, *k5, *k6; + + k1 = yang_dnode_exists(dnode, "./K1") ? + yang_dnode_get_string(dnode, "./K1") : "0"; + k2 = yang_dnode_exists(dnode, "./K2") ? + yang_dnode_get_string(dnode, "./K2") : "0"; + k3 = yang_dnode_exists(dnode, "./K3") ? + yang_dnode_get_string(dnode, "./K3") : "0"; + k4 = yang_dnode_exists(dnode, "./K4") ? + yang_dnode_get_string(dnode, "./K4") : "0"; + k5 = yang_dnode_exists(dnode, "./K5") ? + yang_dnode_get_string(dnode, "./K5") : "0"; + k6 = yang_dnode_exists(dnode, "./K6") ? + yang_dnode_get_string(dnode, "./K6") : "0"; + + vty_out(vty, " metric weights %s %s %s %s %s", + k1, k2, k3, k4, k5); + if (k6) + vty_out(vty, " %s", k6); + vty_out(vty, "\n"); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/network + */ +DEFPY_YANG( + eigrp_network, + eigrp_network_cmd, + "[no] network A.B.C.D/M$prefix", + NO_STR + "Enable routing on an IP network\n" + "EIGRP network prefix\n") +{ + if (no) + nb_cli_enqueue_change(vty, "./network", NB_OP_DESTROY, + prefix_str); + else + nb_cli_enqueue_change(vty, "./network", NB_OP_CREATE, + prefix_str); + + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_network(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + const char *prefix = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " network %s\n", prefix); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/neighbor + */ +DEFPY_YANG( + eigrp_neighbor, + eigrp_neighbor_cmd, + "[no] neighbor A.B.C.D$addr", + NO_STR + "Specify a neighbor router\n" + "Neighbor address\n") +{ + if (no) + nb_cli_enqueue_change(vty, "./neighbor", NB_OP_DESTROY, + addr_str); + else + nb_cli_enqueue_change(vty, "./neighbor", NB_OP_CREATE, + addr_str); + + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_neighbor(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + const char *prefix = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " neighbor %s\n", prefix); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/redistribute + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/route-map + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/bandwidth + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/delay + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/reliability + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/load + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/mtu + */ +DEFPY_YANG( + eigrp_redistribute_source_metric, + eigrp_redistribute_source_metric_cmd, + "[no] redistribute " FRR_REDIST_STR_EIGRPD + "$proto [metric (1-4294967295)$bw (0-4294967295)$delay (0-255)$rlbt (1-255)$load (1-65535)$mtu]", + NO_STR + REDIST_STR + FRR_REDIST_HELP_STR_EIGRPD + "Metric for redistributed routes\n" + "Bandwidth metric in Kbits per second\n" + "EIGRP delay metric, in 10 microsecond units\n" + "EIGRP reliability metric where 255 is 100% reliable2 ?\n" + "EIGRP Effective bandwidth metric (Loading) where 255 is 100% loaded\n" + "EIGRP MTU of the path\n") +{ + char xpath[XPATH_MAXLEN], xpath_metric[XPATH_MAXLEN + 64]; + + snprintf(xpath, sizeof(xpath), "./redistribute[protocol='%s']", proto); + + if (no) { + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); + } + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (bw == 0 || delay == 0 || rlbt == 0 || load == 0 || mtu == 0) + return nb_cli_apply_changes(vty, NULL); + + snprintf(xpath_metric, sizeof(xpath_metric), "%s/metrics/bandwidth", + xpath); + nb_cli_enqueue_change(vty, xpath_metric, NB_OP_MODIFY, bw_str); + snprintf(xpath_metric, sizeof(xpath_metric), "%s/metrics/delay", xpath); + nb_cli_enqueue_change(vty, xpath_metric, NB_OP_MODIFY, delay_str); + snprintf(xpath_metric, sizeof(xpath_metric), "%s/metrics/reliability", + xpath); + nb_cli_enqueue_change(vty, xpath_metric, NB_OP_MODIFY, rlbt_str); + snprintf(xpath_metric, sizeof(xpath_metric), "%s/metrics/load", xpath); + nb_cli_enqueue_change(vty, xpath_metric, NB_OP_MODIFY, load_str); + snprintf(xpath_metric, sizeof(xpath_metric), "%s/metrics/mtu", xpath); + nb_cli_enqueue_change(vty, xpath_metric, NB_OP_MODIFY, mtu_str); + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_redistribute(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + const char *proto = yang_dnode_get_string(dnode, "./protocol"); + const char *bw, *delay, *load, *mtu, *rlbt; + + bw = yang_dnode_exists(dnode, "./metrics/bandwidth") ? + yang_dnode_get_string(dnode, "./metrics/bandwidth") : NULL; + delay = yang_dnode_exists(dnode, "./metrics/delay") ? + yang_dnode_get_string(dnode, "./metrics/delay") : NULL; + rlbt = yang_dnode_exists(dnode, "./metrics/reliability") ? + yang_dnode_get_string(dnode, "./metrics/reliability") : NULL; + load = yang_dnode_exists(dnode, "./metrics/load") ? + yang_dnode_get_string(dnode, "./metrics/load") : NULL; + mtu = yang_dnode_exists(dnode, "./metrics/mtu") ? + yang_dnode_get_string(dnode, "./metrics/mtu") : NULL; + + vty_out(vty, " redistribute %s", proto); + if (bw || rlbt || delay || load || mtu) + vty_out(vty, " metric %s %s %s %s %s", bw, delay, rlbt, load, + mtu); + vty_out(vty, "\n"); +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/delay + */ +DEFPY_YANG( + eigrp_if_delay, + eigrp_if_delay_cmd, + "delay (1-16777215)$delay", + "Specify interface throughput delay\n" + "Throughput delay (tens of microseconds)\n") +{ + nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/delay", + NB_OP_MODIFY, delay_str); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_if_delay, + no_eigrp_if_delay_cmd, + "no delay [(1-16777215)]", + NO_STR + "Specify interface throughput delay\n" + "Throughput delay (tens of microseconds)\n") +{ + nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/delay", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_delay(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + const char *delay = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " delay %s\n", delay); +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/bandwidth + */ +DEFPY_YANG( + eigrp_if_bandwidth, + eigrp_if_bandwidth_cmd, + "eigrp bandwidth (1-10000000)$bw", + EIGRP_STR + "Set bandwidth informational parameter\n" + "Bandwidth in kilobits\n") +{ + nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/bandwidth", + NB_OP_MODIFY, bw_str); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_if_bandwidth, + no_eigrp_if_bandwidth_cmd, + "no eigrp bandwidth [(1-10000000)]", + NO_STR + EIGRP_STR + "Set bandwidth informational parameter\n" + "Bandwidth in kilobits\n") +{ + nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/bandwidth", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_bandwidth(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + const char *bandwidth = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " eigrp bandwidth %s\n", bandwidth); +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/hello-interval + */ +DEFPY_YANG( + eigrp_if_ip_hellointerval, + eigrp_if_ip_hellointerval_cmd, + "ip hello-interval eigrp (1-65535)$hello", + "Interface Internet Protocol config commands\n" + "Configures EIGRP hello interval\n" + EIGRP_STR + "Seconds between hello transmissions\n") +{ + nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/hello-interval", + NB_OP_MODIFY, hello_str); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_if_ip_hellointerval, + no_eigrp_if_ip_hellointerval_cmd, + "no ip hello-interval eigrp [(1-65535)]", + NO_STR + "Interface Internet Protocol config commands\n" + "Configures EIGRP hello interval\n" + EIGRP_STR + "Seconds between hello transmissions\n") +{ + nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/hello-interval", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + + +void eigrp_cli_show_hello_interval(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + const char *hello = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " ip hello-interval eigrp %s\n", hello); +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/hold-time + */ +DEFPY_YANG( + eigrp_if_ip_holdinterval, + eigrp_if_ip_holdinterval_cmd, + "ip hold-time eigrp (1-65535)$hold", + "Interface Internet Protocol config commands\n" + "Configures EIGRP IPv4 hold time\n" + EIGRP_STR + "Seconds before neighbor is considered down\n") +{ + nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/hold-time", + NB_OP_MODIFY, hold_str); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_if_ip_holdinterval, + no_eigrp_if_ip_holdinterval_cmd, + "no ip hold-time eigrp [(1-65535)]", + NO_STR + "Interface Internet Protocol config commands\n" + "Configures EIGRP IPv4 hold time\n" + EIGRP_STR + "Seconds before neighbor is considered down\n") +{ + nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/hold-time", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_hold_time(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + const char *holdtime = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " ip hold-time eigrp %s\n", holdtime); +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/split-horizon + */ +/* NOT implemented. */ + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/summarize-addresses + */ +DEFPY_YANG( + eigrp_ip_summary_address, + eigrp_ip_summary_address_cmd, + "ip summary-address eigrp (1-65535)$as A.B.C.D/M$prefix", + "Interface Internet Protocol config commands\n" + "Perform address summarization\n" + EIGRP_STR + AS_STR + "Summary <network>/<length>, e.g. 192.168.0.0/16\n") +{ + char xpath[XPATH_MAXLEN], xpath_auth[XPATH_MAXLEN + 64]; + + snprintf(xpath, sizeof(xpath), "./frr-eigrpd:eigrp/instance[asn='%s']", + as_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_auth, sizeof(xpath_auth), "%s/summarize-addresses", xpath); + nb_cli_enqueue_change(vty, xpath_auth, NB_OP_CREATE, prefix_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_ip_summary_address, + no_eigrp_ip_summary_address_cmd, + "no ip summary-address eigrp (1-65535)$as A.B.C.D/M$prefix", + NO_STR + "Interface Internet Protocol config commands\n" + "Perform address summarization\n" + EIGRP_STR + AS_STR + "Summary <network>/<length>, e.g. 192.168.0.0/16\n") +{ + char xpath[XPATH_MAXLEN], xpath_auth[XPATH_MAXLEN + 64]; + + snprintf(xpath, sizeof(xpath), "./frr-eigrpd:eigrp/instance[asn='%s']", + as_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_auth, sizeof(xpath_auth), "%s/summarize-addresses", xpath); + nb_cli_enqueue_change(vty, xpath_auth, NB_OP_DESTROY, prefix_str); + + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_summarize_address(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + const struct lyd_node *instance = + yang_dnode_get_parent(dnode, "instance"); + uint16_t asn = yang_dnode_get_uint16(instance, "./asn"); + const char *summarize_address = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " ip summary-address eigrp %d %s\n", asn, + summarize_address); +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/authentication + */ +DEFPY_YANG( + eigrp_authentication_mode, + eigrp_authentication_mode_cmd, + "ip authentication mode eigrp (1-65535)$as <md5|hmac-sha-256>$crypt", + "Interface Internet Protocol config commands\n" + "Authentication subcommands\n" + "Mode\n" + EIGRP_STR + AS_STR + "Keyed message digest\n" + "HMAC SHA256 algorithm \n") +{ + char xpath[XPATH_MAXLEN], xpath_auth[XPATH_MAXLEN + 64]; + + snprintf(xpath, sizeof(xpath), "./frr-eigrpd:eigrp/instance[asn='%s']", + as_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_auth, sizeof(xpath_auth), "%s/authentication", xpath); + nb_cli_enqueue_change(vty, xpath_auth, NB_OP_MODIFY, crypt); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_authentication_mode, + no_eigrp_authentication_mode_cmd, + "no ip authentication mode eigrp (1-65535)$as [<md5|hmac-sha-256>]", + NO_STR + "Interface Internet Protocol config commands\n" + "Authentication subcommands\n" + "Mode\n" + EIGRP_STR + AS_STR + "Keyed message digest\n" + "HMAC SHA256 algorithm \n") +{ + char xpath[XPATH_MAXLEN], xpath_auth[XPATH_MAXLEN + 64]; + + snprintf(xpath, sizeof(xpath), "./frr-eigrpd:eigrp/instance[asn='%s']", + as_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_auth, sizeof(xpath_auth), "%s/authentication", xpath); + nb_cli_enqueue_change(vty, xpath_auth, NB_OP_MODIFY, "none"); + + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_authentication(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + const struct lyd_node *instance = + yang_dnode_get_parent(dnode, "instance"); + uint16_t asn = yang_dnode_get_uint16(instance, "./asn"); + const char *crypt = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " ip authentication mode eigrp %d %s\n", asn, crypt); +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/keychain + */ +DEFPY_YANG( + eigrp_authentication_keychain, + eigrp_authentication_keychain_cmd, + "ip authentication key-chain eigrp (1-65535)$as WORD$name", + "Interface Internet Protocol config commands\n" + "Authentication subcommands\n" + "Key-chain\n" + EIGRP_STR + AS_STR + "Name of key-chain\n") +{ + char xpath[XPATH_MAXLEN], xpath_auth[XPATH_MAXLEN + 64]; + + snprintf(xpath, sizeof(xpath), "./frr-eigrpd:eigrp/instance[asn='%s']", + as_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_auth, sizeof(xpath_auth), "%s/keychain", xpath); + nb_cli_enqueue_change(vty, xpath_auth, NB_OP_MODIFY, name); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_authentication_keychain, + no_eigrp_authentication_keychain_cmd, + "no ip authentication key-chain eigrp (1-65535)$as [WORD]", + NO_STR + "Interface Internet Protocol config commands\n" + "Authentication subcommands\n" + "Key-chain\n" + EIGRP_STR + AS_STR + "Name of key-chain\n") +{ + char xpath[XPATH_MAXLEN], xpath_auth[XPATH_MAXLEN + 64]; + + snprintf(xpath, sizeof(xpath), "./frr-eigrpd:eigrp/instance[asn='%s']", + as_str); + snprintf(xpath_auth, sizeof(xpath_auth), "%s/keychain", xpath); + nb_cli_enqueue_change(vty, xpath_auth, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_keychain(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + const struct lyd_node *instance = + yang_dnode_get_parent(dnode, "instance"); + uint16_t asn = yang_dnode_get_uint16(instance, "./asn"); + const char *keychain = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " ip authentication key-chain eigrp %d %s\n", asn, + keychain); +} + + +/* + * CLI installation procedures. + */ +static int eigrp_config_write(struct vty *vty); +static struct cmd_node eigrp_node = { + .name = "eigrp", + .node = EIGRP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", + .config_write = eigrp_config_write, +}; + +static int eigrp_config_write(struct vty *vty) +{ + const struct lyd_node *dnode; + int written = 0; + + dnode = yang_dnode_get(running_config->dnode, "/frr-eigrpd:eigrpd"); + if (dnode) { + nb_cli_show_dnode_cmds(vty, dnode, false); + written = 1; + } + + return written; +} + +void +eigrp_cli_init(void) +{ + install_element(CONFIG_NODE, &router_eigrp_cmd); + install_element(CONFIG_NODE, &no_router_eigrp_cmd); + + install_node(&eigrp_node); + install_default(EIGRP_NODE); + + install_element(EIGRP_NODE, &eigrp_router_id_cmd); + install_element(EIGRP_NODE, &no_eigrp_router_id_cmd); + install_element(EIGRP_NODE, &eigrp_passive_interface_cmd); + install_element(EIGRP_NODE, &eigrp_timers_active_cmd); + install_element(EIGRP_NODE, &no_eigrp_timers_active_cmd); + install_element(EIGRP_NODE, &eigrp_variance_cmd); + install_element(EIGRP_NODE, &no_eigrp_variance_cmd); + install_element(EIGRP_NODE, &eigrp_maximum_paths_cmd); + install_element(EIGRP_NODE, &no_eigrp_maximum_paths_cmd); + install_element(EIGRP_NODE, &eigrp_metric_weights_cmd); + install_element(EIGRP_NODE, &no_eigrp_metric_weights_cmd); + install_element(EIGRP_NODE, &eigrp_network_cmd); + install_element(EIGRP_NODE, &eigrp_neighbor_cmd); + install_element(EIGRP_NODE, &eigrp_redistribute_source_metric_cmd); + + vrf_cmd_init(NULL); + + if_cmd_init_default(); + + install_element(INTERFACE_NODE, &eigrp_if_delay_cmd); + install_element(INTERFACE_NODE, &no_eigrp_if_delay_cmd); + install_element(INTERFACE_NODE, &eigrp_if_bandwidth_cmd); + install_element(INTERFACE_NODE, &no_eigrp_if_bandwidth_cmd); + install_element(INTERFACE_NODE, &eigrp_if_ip_hellointerval_cmd); + install_element(INTERFACE_NODE, &no_eigrp_if_ip_hellointerval_cmd); + install_element(INTERFACE_NODE, &eigrp_if_ip_holdinterval_cmd); + install_element(INTERFACE_NODE, &no_eigrp_if_ip_holdinterval_cmd); + install_element(INTERFACE_NODE, &eigrp_ip_summary_address_cmd); + install_element(INTERFACE_NODE, &no_eigrp_ip_summary_address_cmd); + install_element(INTERFACE_NODE, &eigrp_authentication_mode_cmd); + install_element(INTERFACE_NODE, &no_eigrp_authentication_mode_cmd); + install_element(INTERFACE_NODE, &eigrp_authentication_keychain_cmd); + install_element(INTERFACE_NODE, &no_eigrp_authentication_keychain_cmd); +} diff --git a/eigrpd/eigrp_cli.h b/eigrpd/eigrp_cli.h new file mode 100644 index 0000000..ed7b274 --- /dev/null +++ b/eigrpd/eigrp_cli.h @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP CLI Functions. + * Copyright (C) 2019 + * Authors: + * Donnie Savage + */ + +#ifndef _EIGRP_CLI_H_ +#define _EIGRP_CLI_H_ + +/*Prototypes*/ +extern void eigrp_cli_show_header(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_end_header(struct vty *vty, + const struct lyd_node *dnode); +extern void eigrp_cli_show_router_id(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_passive_interface(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_active_time(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_variance(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_maximum_paths(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_metrics(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_network(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_neighbor(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_redistribute(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_delay(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_bandwidth(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_hello_interval(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_hold_time(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_summarize_address(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_authentication(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_keychain(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_init(void); + +#endif /*EIGRP_CLI_H_ */ diff --git a/eigrpd/eigrp_const.h b/eigrpd/eigrp_const.h new file mode 100644 index 0000000..607719d --- /dev/null +++ b/eigrpd/eigrp_const.h @@ -0,0 +1,439 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Definition of Constants. + * Copyright (C) 2013-2016 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * Frantisek Gazo + * Tomas Hvorkovy + * Martin Kontsek + * Lukas Koribsky + */ + +#ifndef _ZEBRA_EIGRP_CONST_H_ +#define _ZEBRA_EIGRP_CONST_H_ + +#define EIGRP_NEIGHBOR_DOWN 0 +#define EIGRP_NEIGHBOR_PENDING 1 +#define EIGRP_NEIGHBOR_UP 2 +#define EIGRP_NEIGHBOR_STATE_MAX 3 + +/*Packet requiring ack will be retransmitted again after this time*/ +#define EIGRP_PACKET_RETRANS_TIME 2 /* in seconds */ +#define EIGRP_PACKET_RETRANS_MAX 16 /* number of retrans attempts */ +#define PLAINTEXT_LENGTH 81 + +/*Metric variance multiplier*/ +#define EIGRP_VARIANCE_DEFAULT 1 +#define EIGRP_MAX_PATHS_DEFAULT 4 + +/* Return values of functions involved in packet verification */ +#define MSG_OK 0 +#define MSG_NG 1 + +#define EIGRP_HEADER_VERSION 2 + +/* Default protocol, port number. */ +#ifndef IPPROTO_EIGRPIGP +#define IPPROTO_EIGRPIGP 88 +#endif /* IPPROTO_EIGRPIGP */ + +#define EIGRP_AUTH_MD5_TLV_SIZE 40 +#define EIGRP_AUTH_SHA256_TLV_SIZE 56 + +/*Cisco routers use only first 44 bytes of basic hello for their MD5 + * calculations*/ +#define EIGRP_MD5_BASIC_COMPUTE 44 +#define EIGRP_MD5_UPDATE_INIT_COMPUTE 40 + +#define EIGRP_AUTH_BASIC_HELLO_FLAG 0x01 +#define EIGRP_AUTH_TID_HELLO_FLAG 0x02 +#define EIGRP_AUTH_UPDATE_INIT_FLAG 0x04 +#define EIGRP_AUTH_UPDATE_FLAG 0x08 +#define EIGRP_AUTH_EXTRA_SALT_FLAG 0x10 + +#define EIGRP_NEXT_SEQUENCE_TLV_SIZE 8 + +/* IP TTL for EIGRP protocol. */ +#define EIGRP_IP_TTL 1 + +/* VTY port number. */ +#define EIGRP_VTY_PORT 2613 + +/* Default configuration file name for eigrp. */ +#define EIGRP_DEFAULT_CONFIG "eigrpd.conf" + +#define EIGRP_HELLO_INTERVAL_DEFAULT 5 +#define EIGRP_HOLD_INTERVAL_DEFAULT 15 +#define EIGRP_BANDWIDTH_DEFAULT 100000 +#define EIGRP_DELAY_DEFAULT 10 +#define EIGRP_RELIABILITY_DEFAULT 255 +#define EIGRP_LOAD_DEFAULT 1 + +#define EIGRP_MULTICAST_ADDRESS 0xe000000A /*224.0.0.10*/ + +#define EIGRP_MAX_METRIC 0xffffffffU /*4294967295*/ +enum metric_change { METRIC_DECREASE, METRIC_SAME, METRIC_INCREASE }; + +#define DEFAULT_ROUTE ZEBRA_ROUTE_MAX +#define DEFAULT_ROUTE_TYPE(T) ((T) == DEFAULT_ROUTE) + +#define INTERFACE_DOWN_BY_ZEBRA 1 +#define INTERFACE_DOWN_BY_VTY 2 +#define INTERFACE_DOWN_BY_FINAL 3 + +#define EIGRP_HELLO_NORMAL 0x00 +#define EIGRP_HELLO_GRACEFUL_SHUTDOWN 0x01 +#define EIGRP_HELLO_ADD_SEQUENCE 0x02 +#define EIGRP_HELLO_GRACEFUL_SHUTDOWN_NBR 0x04 + +/* EIGRP Network Type. */ +#define EIGRP_IFTYPE_NONE 0 +#define EIGRP_IFTYPE_POINTOPOINT 1 +#define EIGRP_IFTYPE_BROADCAST 2 +#define EIGRP_IFTYPE_LOOPBACK 3 +#define EIGRP_IFTYPE_MAX 4 + +#define EIGRP_IF_ACTIVE 0 +#define EIGRP_IF_PASSIVE 1 + +/* EIGRP TT destination type */ +#define EIGRP_TOPOLOGY_TYPE_CONNECTED 0 // Connected network +#define EIGRP_TOPOLOGY_TYPE_REMOTE 1 // Remote internal network +#define EIGRP_TOPOLOGY_TYPE_REMOTE_EXTERNAL 2 // Remote external network + +/*EIGRP TT entry flags*/ +#define EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG (1 << 0) +#define EIGRP_ROUTE_DESCRIPTOR_FSUCCESSOR_FLAG (1 << 1) +#define EIGRP_ROUTE_DESCRIPTOR_INTABLE_FLAG (1 << 2) +#define EIGRP_ROUTE_DESCRIPTOR_EXTERNAL_FLAG (1 << 3) + +/*EIGRP FSM state count, event count*/ +#define EIGRP_FSM_STATE_MAX 5 +#define EIGRP_FSM_EVENT_MAX 16 + +/*EEGRP FSM states*/ +enum eigrp_fsm_states { + EIGRP_FSM_STATE_PASSIVE, + EIGRP_FSM_STATE_ACTIVE_0, + EIGRP_FSM_STATE_ACTIVE_1, + EIGRP_FSM_STATE_ACTIVE_2, + EIGRP_FSM_STATE_ACTIVE_3, +}; + +/*EIGRP FSM events return values*/ +#define EIGRP_FSM_NEED_UPDATE 1 +#define EIGRP_FSM_NEED_QUERY 2 + +/*EIGRP FSM events*/ +enum eigrp_fsm_events { + /* + * Input event other than query from succ, + * FC is not satisfied + */ + EIGRP_FSM_EVENT_NQ_FCN, + + /* last reply, FD is reset */ + EIGRP_FSM_EVENT_LR, + + /* Query from succ, FC not satisfied */ + EIGRP_FSM_EVENT_Q_FCN, + + /* last reply, FC satisifed with current value of FDij */ + EIGRP_FSM_EVENT_LR_FCS, + + /* distance increase while in a active state */ + EIGRP_FSM_EVENT_DINC, + + /* Query from succ while in active state */ + EIGRP_FSM_EVENT_QACT, + + /* last reply, FC not satisfied */ + EIGRP_FSM_EVENT_LR_FCN, + + /* + * state not changed + * usually by receiving not last reply + */ + EIGRP_FSM_KEEP_STATE, +}; + +/** + * External routes originate from some other protocol - these are them + */ +#define NULL_PROTID 0 /*!< unknown protocol */ +#define IGRP_PROTID 1 /*!< IGRP.. whos your daddy! */ +#define EIGRP_PROTID 2 /*!< EIGRP - Just flat out the best */ +#define STATIC_PROTID 3 /*!< Staticly configured source */ +#define RIP_PROTID 4 /*!< Routing Information Protocol */ +#define HELLO_PROTID 5 /*!< Hello? RFC-891 you there? */ +#define OSPF_PROTID 6 /*!< OSPF - Open Shortest Path First */ +#define ISIS_PROTID 7 /*!< Intermediate System To Intermediate System */ +#define EGP_PROTID 8 /*!< Exterior Gateway Protocol */ +#define BGP_PROTID 9 /*!< Border Gateway Protocol */ +#define IDRP_PROTID 10 /*!< InterDomain Routing Protocol */ +#define CONN_PROTID 11 /*!< Connected source */ + +/* + * metric k-value defaults + */ +#define EIGRP_K1_DEFAULT 1 //!< unweighed inverse bandwidth +#define EIGRP_K2_DEFAULT 0 //!< no loading term +#define EIGRP_K3_DEFAULT 1 //!< unweighted delay +#define EIGRP_K4_DEFAULT 0 //!< no reliability term +#define EIGRP_K5_DEFAULT 0 //!< no reliability term +#define EIGRP_K6_DEFAULT 0 //!< do not add in extended metrics + +/* + * EIGRP Fixed header + */ +#define EIGRP_HEADER_LEN 20U +#define EIGRP_PACKET_MAX_LEN 65535U /* includes IP Header size. */ + +#define EIGRP_TLV_HDR_LENGTH 4 + +/** + * EIGRP Packet Opcodes + */ +#define EIGRP_OPC_UPDATE 1 /*!< packet containing routing information */ +#define EIGRP_OPC_REQUEST 2 /*!< sent to request one or more routes */ +#define EIGRP_OPC_QUERY 3 /*!< sent when a routing is in active start */ +#define EIGRP_OPC_REPLY 4 /*!< sent in response to a query */ +#define EIGRP_OPC_HELLO 5 /*!< sent to maintain a peering session */ +#define EIGRP_OPC_IPXSAP 6 /*!< IPX SAP information */ +#define EIGRP_OPC_PROBE 7 /*!< for test purposes */ +#define EIGRP_OPC_ACK 8 /*!< acknowledge */ +#define EIGRP_OPC_SIAQUERY 10 /*!< QUERY - with relaxed restrictions */ +#define EIGRP_OPC_SIAREPLY 11 /*!< REPLY - may contain old routing information */ + +/** + * EIGRP TLV Range definitions + * PDM TLV Range + * General 0x0000 + * IPv4 0x0100 ** TLVs for one and all + * ATALK 0x0200 ** legacy + * IPX 0x0300 ** discontinued + * IPv6 0x0400 ** legacy + * Multiprotocol 0x0600 ** wide metrics + * MultiTopology 0x00f0 ** deprecated + */ +#define EIGRP_TLV_RANGEMASK 0xfff0 /*!< should be 0xff00 - opps */ +#define EIGRP_TLV_GENERAL 0x0000 + +/** + * 1.2 TLV Definitions ** legacy + * These are considered legacyu and are only used for backward compability with + * older Cisco Routers. They should not be your first choice for packet codings + */ +#define EIGRP_TLV_IPv4 0x0100 /*!< Classic IPv4 TLV encoding */ +#define EIGRP_TLV_ATALK 0x0200 /*!< Classic Appletalk TLV encoding*/ +#define EIGRP_TLV_IPX 0x0300 /*!< Classic IPX TLV encoding */ +#define EIGRP_TLV_IPv6 0x0400 /*!< Classic IPv6 TLV encoding */ + +/** + * 2.0 Multi-Protocol TLV Definitions + * These are the current packet formats and should be used for packets + */ +#define EIGRP_TLV_MP 0x0600 /*!< Non-PDM specific encoding */ + +/** + * TLV type definitions. Generic (protocol-independent) TLV types are + * defined here. Protocol-specific ones are defined elsewhere. + */ +#define EIGRP_TLV_PARAMETER (EIGRP_TLV_GENERAL | 0x0001) /*!< eigrp parameters */ +#define EIGRP_TLV_PARAMETER_LEN (12U) +#define EIGRP_TLV_AUTH (EIGRP_TLV_GENERAL | 0x0002) /*!< authentication */ +#define EIGRP_TLV_SEQ (EIGRP_TLV_GENERAL | 0x0003) /*!< sequenced packet */ +#define EIGRP_TLV_SEQ_BASE_LEN (5U) +#define EIGRP_TLV_SW_VERSION (EIGRP_TLV_GENERAL | 0x0004) /*!< software version */ +#define EIGRP_TLV_SW_VERSION_LEN (8U) +#define EIGRP_TLV_NEXT_MCAST_SEQ (EIGRP_TLV_GENERAL | 0x0005) /*!< sequence number */ +#define EIGRP_TLV_PEER_TERMINATION (EIGRP_TLV_GENERAL | 0x0007) /*!< peer termination */ +#define EIGRP_TLV_PEER_TERMINATION_LEN (9U) +#define EIGRP_TLV_PEER_TIDLIST (EIGRP_TLV_GENERAL | 0x0008) /*!< peer sub-topology list */ + +/* Older cisco routers send TIDLIST value wrong, adding for backwards + * compatabily */ +#define EIGRP_TLV_PEER_MTRLIST (EIGRP_TLV_GENERAL | 0x00f5) + +/** + * Route Based TLVs + */ +#define EIGRP_TLV_REQUEST 0x0001 +#define EIGRP_TLV_INTERNAL 0x0002 +#define EIGRP_TLV_EXTERNAL 0x0003 +#define EIGRP_TLV_COMMUNITY 0x0004 +#define EIGRP_TLV_TYPEMASK 0x000f + +#define EIGRP_TLV_IPv4_REQ (EIGRP_TLV_IPv4 | EIGRP_TLV_REQUEST) +#define EIGRP_TLV_IPv4_INT (EIGRP_TLV_IPv4 | EIGRP_TLV_INTERNAL) +#define EIGRP_TLV_IPv4_EXT (EIGRP_TLV_IPv4 | EIGRP_TLV_EXTERNAL) +#define EIGRP_TLV_IPv4_COM (EIGRP_TLV_IPv4 | EIGRP_TLV_COMMUNITY) + +#define EIGRP_TLV_IPV4_SIZE_GRT_24_BIT 0x001D +#define EIGRP_TLV_IPV4_SIZE_GRT_16_BIT 0x001C +#define EIGRP_TLV_IPV4_SIZE_GRT_8_BIT 0x001B +#define EIGRP_TLV_IPV4_SIZE_GRT_0_BIT 0x001A +#define EIGRP_TLV_MAX_IPV4_BYTE EIGRP_TLV_IPV4_SIZE_GRT_24_BIT + +/* max number of TLV IPv4 prefixes in packet */ +#define EIGRP_TLV_MAX_IPv4 25 + +/** + * + * extdata flag field definitions + */ +#define EIGRP_OPAQUE_EXT 0x01 /*!< Route is external */ +#define EIGRP_OPAQUE_CD 0x02 /*!< Candidate default route */ + +/** + * Address-Family types are taken from: + * http://www.iana.org/assignments/address-family-numbers + * to provide a standards based exchange of AFI information between + * EIGRP routers. + */ +#define EIGRP_AF_IPv4 1 /*!< IPv4 (IP version 4) */ +#define EIGRP_AF_IPv6 2 /*!< IPv6 (IP version 6) */ +#define EIGRP_AF_IPX 11 /*!< IPX */ +#define EIGRP_AF_ATALK 12 /*!< Appletalk */ +#define EIGRP_SF_COMMON 16384 /*!< Cisco Service Family */ +#define EIGRP_SF_IPv4 16385 /*!< Cisco IPv4 Service Family */ +#define EIGRP_SF_IPv6 16386 /*!< Cisco IPv6 Service Family */ + +/** + * Authentication types supported by EIGRP + */ +#define EIGRP_AUTH_TYPE_NONE 0 +#define EIGRP_AUTH_TYPE_TEXT 1 +#define EIGRP_AUTH_TYPE_MD5 2 +#define EIGRP_AUTH_TYPE_MD5_LEN 16 +#define EIGRP_AUTH_TYPE_SHA256 3 +#define EIGRP_AUTH_TYPE_SHA256_LEN 32 + +/** + * opaque flag field definitions + */ +#define EIGRP_OPAQUE_SRCWD 0x01 /*!< Route Source Withdraw */ +#define EIGRP_OPAQUE_ACTIVE 0x04 /*!< Route is currently in active state */ +#define EIGRP_OPAQUE_REPL 0x08 /*!< Route is replicated from different tableid */ + +/** + * pak flag bit field definitions - 0 (none)-7 source priority + */ +#define EIGRP_PRIV_DEFAULT 0x00 /* 0 (none)-7 source priority */ +#define EIGRP_PRIV_LOW 0x01 +#define EIGRP_PRIV_MEDIUM 0x04 +#define EIGRP_PRIV_HIGH 0x07 + +/* + * Init bit definition. First unicast transmitted Update has this + * bit set in the flags field of the fixed header. It tells the neighbor + * to down-load his topology table. + */ +#define EIGRP_INIT_FLAG 0x01 + +/* + * CR bit (Conditionally Received) definition in flags field on header. Any + * packets with the CR-bit set can be accepted by an EIGRP speaker if and + * only if a previous Hello was received with the SEQUENCE_TYPE TLV present. + * + * This allows multicasts to be transmitted in order and reliably at the + * same time as unicasts are transmitted. + */ +#define EIGRP_CR_FLAG 0x02 + +/* + * RS bit. The Restart flag is set in the hello and the init + * update packets during the nsf signaling period. A nsf-aware + * router looks at the RS flag to detect if a peer is restarting + * and maintain the adjacency. A restarting router looks at + * this flag to determine if the peer is helping out with the restart. + */ +#define EIGRP_RS_FLAG 0x04 + +/* + * EOT bit. The End-of-Table flag marks the end of the start-up updates + * sent to a new peer. A nsf restarting router looks at this flag to + * determine if it has finished receiving the start-up updates from all + * peers. A nsf-aware router waits for this flag before cleaning up + * the stale routes from the restarting peer. + */ +#define EIGRP_EOT_FLAG 0x08 + +/** + * EIGRP Virtual Router ID + * + * Define values to deal with EIGRP virtual router ids. Virtual + * router IDs are stored in the upper short of the EIGRP fixed packet + * header. The lower short of the packet header continues to be used + * as asystem number. + * + * Virtual Router IDs are PDM-independent. All PDMs will use + * VRID_BASE to indicate the 'base' or 'legacy' EIGRP instance. + * All PDMs need to initialize their vrid to VRID_BASE for compatibility + * with legacy routers. + * Once IPv6 supports 'MTR Multicast', it will use the same VRID as + * IPv4. No current plans to support VRIDs on IPX. :) + * Initial usage of VRID is to signal usage of Multicast topology for + * MTR. + * + * VRID_MCAST is a well known constant, other VRIDs will be determined + * programmatic... + * + * With the addition of SAF the VRID space has been divided into two + * segments 0x0000-0x7fff is for EIGRP and vNets, 0x8000-0xffff is + * for saf and its associated vNets. + */ +#define EIGRP_VRID_MASK 0x8001 +#define EIGRP_VRID_AF_BASE 0x0000 +#define EIGRP_VRID_MCAST_BASE 0x0001 +#define EIGRP_VRID_SF_BASE 0x8000 + +/* Extended Attributes for a destination */ +#define EIGRP_ATTR_HDRLEN (2) +#define EIGRP_ATTR_MAXDATA (512) + +#define EIGRP_ATTR_NOOP 0 /*!< No-Op used as offset padding */ +#define EIGRP_ATTR_SCALED 1 /*!< Scaled metric values */ +#define EIGRP_ATTR_TAG 2 /*!< Tag assigned by Admin for dest */ +#define EIGRP_ATTR_COMM 3 /*!< Community attribute for dest */ +#define EIGRP_ATTR_JITTER 4 /*!< Variation in path delay */ +#define EIGRP_ATTR_QENERGY 5 /*!< Non-Active energy usage along path */ +#define EIGRP_ATTR_ENERGY 6 /*!< Active energy usage along path */ + +/* + * Begin EIGRP-BGP interoperability communities + */ +#define EIGRP_EXTCOMM_SOO_ASFMT 0x0003 /* Site-of-Origin, BGP AS format */ +#define EIGRP_EXTCOMM_SOO_ADRFMT 0x0103 /* Site-of-Origin, BGP/EIGRP addr format */ + +/* + * EIGRP Specific communities + */ +#define EIGRP_EXTCOMM_EIGRP 0x8800 /* EIGRP route information appended*/ +#define EIGRP_EXTCOMM_DAD 0x8801 /* EIGRP AS + Delay */ +#define EIGRP_EXTCOMM_VRHB 0x8802 /* EIGRP Vector: Reliability + Hop + BW */ +#define EIGRP_EXTCOMM_SRLM 0x8803 /* EIGRP System: Reserve +Load + MTU */ +#define EIGRP_EXTCOMM_SAR 0x8804 /* EIGRP System: Remote AS + Remote ID */ +#define EIGRP_EXTCOMM_RPM 0x8805 /* EIGRP Remote: Protocol + Metric */ +#define EIGRP_EXTCOMM_VRR 0x8806 /* EIGRP Vecmet: Rsvd + (internal) Routerid */ + +/* + * EIGRP Filter constants + */ +#define EIGRP_FILTER_IN 0 +#define EIGRP_FILTER_OUT 1 +#define EIGRP_FILTER_MAX 2 + +/* + * EIGRP Filter constants + */ +#define EIGRP_HSROLE_DEFAULT EIGRP_HSROLE_SPOKE +#define EIGRP_HSROLE_HUB 0x01 +#define EIGRP_HSROLE_SPOKE 0x02 + +#endif /* _ZEBRA_EIGRP_CONST_H_ */ diff --git a/eigrpd/eigrp_dump.c b/eigrpd/eigrp_dump.c new file mode 100644 index 0000000..2ebabd0 --- /dev/null +++ b/eigrpd/eigrp_dump.c @@ -0,0 +1,566 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Dump Functions and Debugging. + * Copyright (C) 2013-2014 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + */ + +#include <zebra.h> + +#include "linklist.h" +#include "frrevent.h" +#include "prefix.h" +#include "command.h" +#include "stream.h" +#include "log.h" +#include "sockopt.h" +#include "table.h" +#include "keychain.h" + +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_interface.h" +#include "eigrpd/eigrp_neighbor.h" +#include "eigrpd/eigrp_packet.h" +#include "eigrpd/eigrp_zebra.h" +#include "eigrpd/eigrp_vty.h" +#include "eigrpd/eigrp_network.h" +#include "eigrpd/eigrp_dump.h" +#include "eigrpd/eigrp_topology.h" + +/* Enable debug option variables -- valid only session. */ +unsigned long term_debug_eigrp = 0; +unsigned long term_debug_eigrp_nei = 0; +unsigned long term_debug_eigrp_packet[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +unsigned long term_debug_eigrp_zebra = 6; +unsigned long term_debug_eigrp_transmit = 0; + +/* Configuration debug option variables. */ +unsigned long conf_debug_eigrp = 0; +unsigned long conf_debug_eigrp_nei = 0; +unsigned long conf_debug_eigrp_packet[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +unsigned long conf_debug_eigrp_zebra = 0; +unsigned long conf_debug_eigrp_transmit = 0; + + +static int config_write_debug(struct vty *vty) +{ + int write = 0; + int i; + + const char *type_str[] = {"update", "request", "query", "reply", + "hello", "", "probe", "ack", + "", "SIA query", "SIA reply", "stub", + "all"}; + const char *detail_str[] = { + "", " send", " recv", "", + " detail", " send detail", " recv detail", " detail"}; + + + /* debug eigrp event. */ + + /* debug eigrp packet */ + for (i = 0; i < 10; i++) { + if (conf_debug_eigrp_packet[i] == 0 + && term_debug_eigrp_packet[i] == 0) + continue; + + vty_out(vty, "debug eigrp packet %s%s\n", type_str[i], + detail_str[conf_debug_eigrp_packet[i]]); + write = 1; + } + + return write; +} + +static int eigrp_neighbor_packet_queue_sum(struct eigrp_interface *ei) +{ + struct eigrp_neighbor *nbr; + struct listnode *node, *nnode; + int sum; + sum = 0; + + for (ALL_LIST_ELEMENTS(ei->nbrs, node, nnode, nbr)) { + sum += nbr->retrans_queue->count; + } + + return sum; +} + +/* + * Expects header to be in host order + */ +void eigrp_ip_header_dump(struct ip *iph) +{ + /* IP Header dump. */ + zlog_debug("ip_v %u", iph->ip_v); + zlog_debug("ip_hl %u", iph->ip_hl); + zlog_debug("ip_tos %u", iph->ip_tos); + zlog_debug("ip_len %u", iph->ip_len); + zlog_debug("ip_id %u", (uint32_t)iph->ip_id); + zlog_debug("ip_off %u", (uint32_t)iph->ip_off); + zlog_debug("ip_ttl %u", iph->ip_ttl); + zlog_debug("ip_p %u", iph->ip_p); + zlog_debug("ip_sum 0x%x", (uint32_t)iph->ip_sum); + zlog_debug("ip_src %pI4", &iph->ip_src); + zlog_debug("ip_dst %pI4", &iph->ip_dst); +} + +/* + * Expects header to be in host order + */ +void eigrp_header_dump(struct eigrp_header *eigrph) +{ + /* EIGRP Header dump. */ + zlog_debug("eigrp_version %u", eigrph->version); + zlog_debug("eigrp_opcode %u", eigrph->opcode); + zlog_debug("eigrp_checksum 0x%x", ntohs(eigrph->checksum)); + zlog_debug("eigrp_flags 0x%x", ntohl(eigrph->flags)); + zlog_debug("eigrp_sequence %u", ntohl(eigrph->sequence)); + zlog_debug("eigrp_ack %u", ntohl(eigrph->ack)); + zlog_debug("eigrp_vrid %u", ntohs(eigrph->vrid)); + zlog_debug("eigrp_AS %u", ntohs(eigrph->ASNumber)); +} + +const char *eigrp_if_name_string(struct eigrp_interface *ei) +{ + if (!ei) + return "inactive"; + + return ei->ifp->name; +} + +void show_ip_eigrp_interface_header(struct vty *vty, struct eigrp *eigrp) +{ + + vty_out(vty, + "\nEIGRP interfaces for AS(%d)\n\n%-16s %-10s %-10s %-6s %-12s %-7s %-14s %-12s %-8s %-8s %-8s\n %-44s %-12s %-7s %-14s %-12s %-8s\n", + eigrp->AS, "Interface", "Bandwidth", "Delay", "Peers", + "Xmit Queue", "Mean", "Pacing Time", "Multicast", "Pending", + "Hello", "Holdtime", "", "Un/Reliable", "SRTT", "Un/Reliable", + "Flow Timer", "Routes"); +} + +void show_ip_eigrp_interface_sub(struct vty *vty, struct eigrp *eigrp, + struct eigrp_interface *ei) +{ + vty_out(vty, "%-16s ", IF_NAME(ei)); + vty_out(vty, "%-11u", ei->params.bandwidth); + vty_out(vty, "%-11u", ei->params.delay); + vty_out(vty, "%-7u", ei->nbrs->count); + vty_out(vty, "%u %c %-10u", 0, '/', + eigrp_neighbor_packet_queue_sum(ei)); + vty_out(vty, "%-7u %-14u %-12u %-8u", 0, 0, 0, 0); + vty_out(vty, "%-8u %-8u \n", ei->params.v_hello, ei->params.v_wait); +} + +void show_ip_eigrp_interface_detail(struct vty *vty, struct eigrp *eigrp, + struct eigrp_interface *ei) +{ + vty_out(vty, "%-2s %s %d %-3s \n", "", "Hello interval is ", 0, " sec"); + vty_out(vty, "%-2s %s %s \n", "", "Next xmit serial", "<none>"); + vty_out(vty, "%-2s %s %d %s %d %s %d %s %d \n", "", + "Un/reliable mcasts: ", 0, "/", 0, "Un/reliable ucasts: ", 0, + "/", 0); + vty_out(vty, "%-2s %s %d %s %d %s %d \n", "", "Mcast exceptions: ", 0, + " CR packets: ", 0, " ACKs suppressed: ", 0); + vty_out(vty, "%-2s %s %d %s %d \n", "", "Retransmissions sent: ", 0, + "Out-of-sequence rcvd: ", 0); + vty_out(vty, "%-2s %s %s %s \n", "", "Authentication mode is ", "not", + "set"); + vty_out(vty, "%-2s %s \n", "", "Use multicast"); +} + +void show_ip_eigrp_neighbor_header(struct vty *vty, struct eigrp *eigrp) +{ + vty_out(vty, + "\nEIGRP neighbors for AS(%d)\n\n%-3s %-17s %-20s %-6s %-8s %-6s %-5s %-5s %-5s\n %-41s %-6s %-8s %-6s %-4s %-6s %-5s \n", + eigrp->AS, "H", "Address", "Interface", "Hold", "Uptime", + "SRTT", "RTO", "Q", "Seq", "", "(sec)", "", "(ms)", "", "Cnt", + "Num"); +} + +void show_ip_eigrp_neighbor_sub(struct vty *vty, struct eigrp_neighbor *nbr, + int detail) +{ + + vty_out(vty, "%-3u %-17pI4 %-21s", 0, &nbr->src, IF_NAME(nbr->ei)); + if (nbr->t_holddown) + vty_out(vty, "%-7lu", + event_timer_remain_second(nbr->t_holddown)); + else + vty_out(vty, "- "); + vty_out(vty, "%-8u %-6u %-5u", 0, 0, EIGRP_PACKET_RETRANS_TIME); + vty_out(vty, "%-7lu", nbr->retrans_queue->count); + vty_out(vty, "%u\n", nbr->recv_sequence_number); + + + if (detail) { + vty_out(vty, " Version %u.%u/%u.%u", nbr->os_rel_major, + nbr->os_rel_minor, nbr->tlv_rel_major, + nbr->tlv_rel_minor); + vty_out(vty, ", Retrans: %lu, Retries: %lu", + nbr->retrans_queue->count, 0UL); + vty_out(vty, ", %s\n", eigrp_nbr_state_str(nbr)); + } +} + +/* + * Print standard header for show EIGRP topology output + */ +void show_ip_eigrp_topology_header(struct vty *vty, struct eigrp *eigrp) +{ + vty_out(vty, "\nEIGRP Topology Table for AS(%d)/ID(%pI4)\n\n", + eigrp->AS, &eigrp->router_id); + vty_out(vty, + "Codes: P - Passive, A - Active, U - Update, Q - Query, R - Reply\n r - reply Status, s - sia Status\n\n"); +} + +void show_ip_eigrp_prefix_descriptor(struct vty *vty, + struct eigrp_prefix_descriptor *tn) +{ + struct list *successors = eigrp_topology_get_successor(tn); + + vty_out(vty, "%-3c", (tn->state > 0) ? 'A' : 'P'); + + vty_out(vty, "%pFX, ", tn->destination); + vty_out(vty, "%u successors, ", (successors) ? successors->count : 0); + vty_out(vty, "FD is %u, serno: %" PRIu64 " \n", tn->fdistance, + tn->serno); + + if (successors) + list_delete(&successors); +} + +void show_ip_eigrp_route_descriptor(struct vty *vty, struct eigrp *eigrp, + struct eigrp_route_descriptor *te, + bool *first) +{ + if (te->reported_distance == EIGRP_MAX_METRIC) + return; + + if (*first) { + show_ip_eigrp_prefix_descriptor(vty, te->prefix); + *first = false; + } + + if (te->adv_router == eigrp->neighbor_self) + vty_out(vty, "%-7s%s, %s\n", " ", "via Connected", + IF_NAME(te->ei)); + else { + vty_out(vty, "%-7s%s%pI4 (%u/%u), %s\n", " ", "via ", + &te->adv_router->src, te->distance, + te->reported_distance, IF_NAME(te->ei)); + } +} + + +DEFUN_NOSH (show_debugging_eigrp, + show_debugging_eigrp_cmd, + "show debugging [eigrp]", + SHOW_STR + DEBUG_STR + EIGRP_STR) +{ + int i; + + vty_out(vty, "EIGRP debugging status:\n"); + + /* Show debug status for events. */ + if (IS_DEBUG_EIGRP(event, EVENT)) + vty_out(vty, " EIGRP event debugging is on\n"); + + /* Show debug status for EIGRP Packets. */ + for (i = 0; i < 11; i++) { + if (i == 8) + continue; + + if (IS_DEBUG_EIGRP_PACKET(i, SEND) + && IS_DEBUG_EIGRP_PACKET(i, RECV)) { + vty_out(vty, " EIGRP packet %s%s debugging is on\n", + lookup_msg(eigrp_packet_type_str, i + 1, NULL), + IS_DEBUG_EIGRP_PACKET(i, PACKET_DETAIL) + ? " detail" + : ""); + } else { + if (IS_DEBUG_EIGRP_PACKET(i, SEND)) + vty_out(vty, + " EIGRP packet %s send%s debugging is on\n", + lookup_msg(eigrp_packet_type_str, i + 1, + NULL), + IS_DEBUG_EIGRP_PACKET(i, PACKET_DETAIL) + ? " detail" + : ""); + if (IS_DEBUG_EIGRP_PACKET(i, RECV)) + vty_out(vty, + " EIGRP packet %s receive%s debugging is on\n", + lookup_msg(eigrp_packet_type_str, i + 1, + NULL), + IS_DEBUG_EIGRP_PACKET(i, PACKET_DETAIL) + ? " detail" + : ""); + } + } + + cmd_show_lib_debugs(vty); + return CMD_SUCCESS; +} + + +/* + [no] debug eigrp packet (hello|dd|ls-request|ls-update|ls-ack|all) + [send|recv [detail]] +*/ + +DEFUN (debug_eigrp_transmit, + debug_eigrp_transmit_cmd, + "debug eigrp transmit <send|recv|all> [detail]", + DEBUG_STR + EIGRP_STR + "EIGRP transmission events\n" + "packet sent\n" + "packet received\n" + "all packets\n" + "Detailed Information\n") +{ + int flag = 0; + int idx = 2; + + /* send or recv. */ + if (argv_find(argv, argc, "send", &idx)) + flag = EIGRP_DEBUG_SEND; + else if (argv_find(argv, argc, "recv", &idx)) + flag = EIGRP_DEBUG_RECV; + else if (argv_find(argv, argc, "all", &idx)) + flag = EIGRP_DEBUG_SEND_RECV; + + /* detail option */ + if (argv_find(argv, argc, "detail", &idx)) + flag = EIGRP_DEBUG_PACKET_DETAIL; + + if (vty->node == CONFIG_NODE) + DEBUG_TRANSMIT_ON(0, flag); + else + TERM_DEBUG_TRANSMIT_ON(0, flag); + + return CMD_SUCCESS; +} + +DEFUN (no_debug_eigrp_transmit, + no_debug_eigrp_transmit_cmd, + "no debug eigrp transmit <send|recv|all> [detail]", + NO_STR + UNDEBUG_STR + EIGRP_STR + "EIGRP transmission events\n" + "packet sent\n" + "packet received\n" + "all packets\n" + "Detailed Information\n") +{ + int flag = 0; + int idx = 3; + + /* send or recv. */ + if (argv_find(argv, argc, "send", &idx)) + flag = EIGRP_DEBUG_SEND; + else if (argv_find(argv, argc, "recv", &idx)) + flag = EIGRP_DEBUG_RECV; + else if (argv_find(argv, argc, "all", &idx)) + flag = EIGRP_DEBUG_SEND_RECV; + + /* detail option */ + if (argv_find(argv, argc, "detail", &idx)) + flag = EIGRP_DEBUG_PACKET_DETAIL; + + if (vty->node == CONFIG_NODE) + DEBUG_TRANSMIT_OFF(0, flag); + else + TERM_DEBUG_TRANSMIT_OFF(0, flag); + + return CMD_SUCCESS; +} + +DEFUN (debug_eigrp_packets, + debug_eigrp_packets_all_cmd, + "debug eigrp packets <siaquery|siareply|ack|hello|probe|query|reply|request|retry|stub|terse|update|all> [send|receive] [detail]", + DEBUG_STR + EIGRP_STR + "EIGRP packets\n" + "EIGRP SIA-Query packets\n" + "EIGRP SIA-Reply packets\n" + "EIGRP ack packets\n" + "EIGRP hello packets\n" + "EIGRP probe packets\n" + "EIGRP query packets\n" + "EIGRP reply packets\n" + "EIGRP request packets\n" + "EIGRP retransmissions\n" + "EIGRP stub packets\n" + "Display all EIGRP packets except Hellos\n" + "EIGRP update packets\n" + "Display all EIGRP packets\n" + "Send Packets\n" + "Receive Packets\n" + "Detail Information\n") +{ + int type = 0; + int flag = 0; + int i; + int idx = 0; + + /* Check packet type. */ + if (argv_find(argv, argc, "hello", &idx)) + type = EIGRP_DEBUG_HELLO; + if (argv_find(argv, argc, "update", &idx)) + type = EIGRP_DEBUG_UPDATE; + if (argv_find(argv, argc, "query", &idx)) + type = EIGRP_DEBUG_QUERY; + if (argv_find(argv, argc, "ack", &idx)) + type = EIGRP_DEBUG_ACK; + if (argv_find(argv, argc, "probe", &idx)) + type = EIGRP_DEBUG_PROBE; + if (argv_find(argv, argc, "stub", &idx)) + type = EIGRP_DEBUG_STUB; + if (argv_find(argv, argc, "reply", &idx)) + type = EIGRP_DEBUG_REPLY; + if (argv_find(argv, argc, "request", &idx)) + type = EIGRP_DEBUG_REQUEST; + if (argv_find(argv, argc, "siaquery", &idx)) + type = EIGRP_DEBUG_SIAQUERY; + if (argv_find(argv, argc, "siareply", &idx)) + type = EIGRP_DEBUG_SIAREPLY; + if (argv_find(argv, argc, "all", &idx)) + type = EIGRP_DEBUG_PACKETS_ALL; + + + /* All packet types, both send and recv. */ + flag = EIGRP_DEBUG_SEND_RECV; + + /* send or recv. */ + if (argv_find(argv, argc, "s", &idx)) + flag = EIGRP_DEBUG_SEND; + else if (argv_find(argv, argc, "r", &idx)) + flag = EIGRP_DEBUG_RECV; + + /* detail. */ + if (argv_find(argv, argc, "detail", &idx)) + flag |= EIGRP_DEBUG_PACKET_DETAIL; + + for (i = 0; i < 11; i++) + if (type & (0x01 << i)) { + if (vty->node == CONFIG_NODE) + DEBUG_PACKET_ON(i, flag); + else + TERM_DEBUG_PACKET_ON(i, flag); + } + + return CMD_SUCCESS; +} + +DEFUN (no_debug_eigrp_packets, + no_debug_eigrp_packets_all_cmd, + "no debug eigrp packets <siaquery|siareply|ack|hello|probe|query|reply|request|retry|stub|terse|update|all> [send|receive] [detail]", + NO_STR + UNDEBUG_STR + EIGRP_STR + "EIGRP packets\n" + "EIGRP SIA-Query packets\n" + "EIGRP SIA-Reply packets\n" + "EIGRP ack packets\n" + "EIGRP hello packets\n" + "EIGRP probe packets\n" + "EIGRP query packets\n" + "EIGRP reply packets\n" + "EIGRP request packets\n" + "EIGRP retransmissions\n" + "EIGRP stub packets\n" + "Display all EIGRP packets except Hellos\n" + "EIGRP update packets\n" + "Display all EIGRP packets\n" + "Send Packets\n" + "Receive Packets\n" + "Detailed Information\n") +{ + int type = 0; + int flag = 0; + int i; + int idx = 0; + + /* Check packet type. */ + if (argv_find(argv, argc, "hello", &idx)) + type = EIGRP_DEBUG_HELLO; + if (argv_find(argv, argc, "update", &idx)) + type = EIGRP_DEBUG_UPDATE; + if (argv_find(argv, argc, "query", &idx)) + type = EIGRP_DEBUG_QUERY; + if (argv_find(argv, argc, "ack", &idx)) + type = EIGRP_DEBUG_ACK; + if (argv_find(argv, argc, "probe", &idx)) + type = EIGRP_DEBUG_PROBE; + if (argv_find(argv, argc, "stub", &idx)) + type = EIGRP_DEBUG_STUB; + if (argv_find(argv, argc, "reply", &idx)) + type = EIGRP_DEBUG_REPLY; + if (argv_find(argv, argc, "request", &idx)) + type = EIGRP_DEBUG_REQUEST; + if (argv_find(argv, argc, "siaquery", &idx)) + type = EIGRP_DEBUG_SIAQUERY; + if (argv_find(argv, argc, "siareply", &idx)) + type = EIGRP_DEBUG_SIAREPLY; + + /* Default, both send and recv. */ + flag = EIGRP_DEBUG_SEND_RECV; + + /* send or recv. */ + if (argv_find(argv, argc, "send", &idx)) + flag = EIGRP_DEBUG_SEND; + else if (argv_find(argv, argc, "reply", &idx)) + flag = EIGRP_DEBUG_RECV; + + /* detail. */ + if (argv_find(argv, argc, "detail", &idx)) + flag |= EIGRP_DEBUG_PACKET_DETAIL; + + for (i = 0; i < 11; i++) + if (type & (0x01 << i)) { + if (vty->node == CONFIG_NODE) + DEBUG_PACKET_OFF(i, flag); + else + TERM_DEBUG_PACKET_OFF(i, flag); + } + + return CMD_SUCCESS; +} + +/* Debug node. */ +static int config_write_debug(struct vty *vty); +static struct cmd_node eigrp_debug_node = { + .name = "debug", + .node = DEBUG_NODE, + .prompt = "", + .config_write = config_write_debug, +}; + +/* Initialize debug commands. */ +void eigrp_debug_init(void) +{ + install_node(&eigrp_debug_node); + + install_element(ENABLE_NODE, &show_debugging_eigrp_cmd); + install_element(ENABLE_NODE, &debug_eigrp_packets_all_cmd); + install_element(ENABLE_NODE, &no_debug_eigrp_packets_all_cmd); + install_element(ENABLE_NODE, &debug_eigrp_transmit_cmd); + install_element(ENABLE_NODE, &no_debug_eigrp_transmit_cmd); + + install_element(CONFIG_NODE, &show_debugging_eigrp_cmd); + install_element(CONFIG_NODE, &debug_eigrp_packets_all_cmd); + install_element(CONFIG_NODE, &no_debug_eigrp_packets_all_cmd); + install_element(CONFIG_NODE, &debug_eigrp_transmit_cmd); + install_element(CONFIG_NODE, &no_debug_eigrp_transmit_cmd); +} diff --git a/eigrpd/eigrp_dump.h b/eigrpd/eigrp_dump.h new file mode 100644 index 0000000..7a952f0 --- /dev/null +++ b/eigrpd/eigrp_dump.h @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Dump Functions and Debbuging. + * Copyright (C) 2013-2014 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + */ + +#ifndef _ZEBRA_EIGRPD_DUMP_H_ +#define _ZEBRA_EIGRPD_DUMP_H_ + +#define EIGRP_TIME_DUMP_SIZE 16 + +/* general debug flags */ +extern unsigned long term_debug_eigrp; +#define EIGRP_DEBUG_EVENT 0x01 +#define EIGRP_DEBUG_DETAIL 0x02 +#define EIGRP_DEBUG_TIMERS 0x04 + +/* neighbor debug flags */ +extern unsigned long term_debug_eigrp_nei; +#define EIGRP_DEBUG_NEI 0x01 + +/* packet debug flags */ +extern unsigned long term_debug_eigrp_packet[]; +#define EIGRP_DEBUG_UPDATE 0x01 +#define EIGRP_DEBUG_REQUEST 0x02 +#define EIGRP_DEBUG_QUERY 0x04 +#define EIGRP_DEBUG_REPLY 0x08 +#define EIGRP_DEBUG_HELLO 0x10 +#define EIGRP_DEBUG_PROBE 0x40 +#define EIGRP_DEBUG_ACK 0x80 +#define EIGRP_DEBUG_SIAQUERY 0x200 +#define EIGRP_DEBUG_SIAREPLY 0x400 +#define EIGRP_DEBUG_STUB 0x800 +#define EIGRP_DEBUG_PACKETS_ALL 0xfff + +extern unsigned long term_debug_eigrp_transmit; +#define EIGRP_DEBUG_SEND 0x01 +#define EIGRP_DEBUG_RECV 0x02 +#define EIGRP_DEBUG_SEND_RECV 0x03 +#define EIGRP_DEBUG_PACKET_DETAIL 0x04 + +/* zebra debug flags */ +extern unsigned long term_debug_eigrp_zebra; +#define EIGRP_DEBUG_ZEBRA_INTERFACE 0x01 +#define EIGRP_DEBUG_ZEBRA_REDISTRIBUTE 0x02 +#define EIGRP_DEBUG_ZEBRA 0x03 + +/* Macro for setting debug option. */ +#define CONF_DEBUG_NEI_ON(a, b) conf_debug_eigrp_nei[a] |= (b) +#define CONF_DEBUG_NEI_OFF(a, b) conf_debug_eigrp_nei[a] &= ~(b) +#define TERM_DEBUG_NEI_ON(a, b) term_debug_eigrp_nei[a] |= (b) +#define TERM_DEBUG_NEI_OFF(a, b) term_debug_eigrp_nei[a] &= ~(b) +#define DEBUG_NEI_ON(a, b) \ + do { \ + CONF_DEBUG_NEI_ON(a, b); \ + TERM_DEBUG_NEI_ON(a, b); \ + } while (0) +#define DEBUG_NEI_OFF(a, b) \ + do { \ + CONF_DEBUG_NEI_OFF(a, b); \ + TERM_DEBUG_NEI_OFF(a, b); \ + } while (0) + +#define CONF_DEBUG_PACKET_ON(a, b) conf_debug_eigrp_packet[a] |= (b) +#define CONF_DEBUG_PACKET_OFF(a, b) conf_debug_eigrp_packet[a] &= ~(b) +#define TERM_DEBUG_PACKET_ON(a, b) term_debug_eigrp_packet[a] |= (b) +#define TERM_DEBUG_PACKET_OFF(a, b) term_debug_eigrp_packet[a] &= ~(b) +#define DEBUG_PACKET_ON(a, b) \ + do { \ + CONF_DEBUG_PACKET_ON(a, b); \ + TERM_DEBUG_PACKET_ON(a, b); \ + } while (0) +#define DEBUG_PACKET_OFF(a, b) \ + do { \ + CONF_DEBUG_PACKET_OFF(a, b); \ + TERM_DEBUG_PACKET_OFF(a, b); \ + } while (0) + +#define CONF_DEBUG_TRANSMIT_ON(a, b) conf_debug_eigrp_transmit |= (b) +#define CONF_DEBUG_TRANSMIT_OFF(a, b) conf_debug_eigrp_transmit &= ~(b) +#define TERM_DEBUG_TRANSMIT_ON(a, b) term_debug_eigrp_transmit |= (b) +#define TERM_DEBUG_TRANSMIT_OFF(a, b) term_debug_eigrp_transmit &= ~(b) +#define DEBUG_TRANSMIT_ON(a, b) \ + do { \ + CONF_DEBUG_TRANSMIT_ON(a, b); \ + TERM_DEBUG_TRANSMIT_ON(a, b); \ + } while (0) +#define DEBUG_TRANSMIT_OFF(a, b) \ + do { \ + CONF_DEBUG_TRANSMIT_OFF(a, b); \ + TERM_DEBUG_TRANSMIT_OFF(a, b); \ + } while (0) + +#define CONF_DEBUG_ON(a, b) conf_debug_eigrp_ ## a |= (EIGRP_DEBUG_ ## b) +#define CONF_DEBUG_OFF(a, b) conf_debug_eigrp_ ## a &= ~(EIGRP_DEBUG_ ## b) +#define TERM_DEBUG_ON(a, b) term_debug_eigrp_ ## a |= (EIGRP_DEBUG_ ## b) +#define TERM_DEBUG_OFF(a, b) term_debug_eigrp_ ## a &= ~(EIGRP_DEBUG_ ## b) +#define DEBUG_ON(a, b) \ + do { \ + CONF_DEBUG_ON(a, b); \ + TERM_DEBUG_ON(a, b); \ + } while (0) +#define DEBUG_OFF(a, b) \ + do { \ + CONF_DEBUG_OFF(a, b); \ + TERM_DEBUG_OFF(a, b); \ + } while (0) + +/* Macro for checking debug option. */ +#define IS_DEBUG_EIGRP_PACKET(a, b) \ + (term_debug_eigrp_packet[a] & EIGRP_DEBUG_##b) +#define IS_DEBUG_EIGRP_TRANSMIT(a, b) \ + (term_debug_eigrp_transmit & EIGRP_DEBUG_##b) +#define IS_DEBUG_EIGRP_NEI(a, b) (term_debug_eigrp_nei & EIGRP_DEBUG_##b) +#define IS_DEBUG_EIGRP(a, b) (term_debug_eigrp & EIGRP_DEBUG_##b) +#define IS_DEBUG_EIGRP_EVENT IS_DEBUG_EIGRP(event, EVENT) + +/* Prototypes. */ +extern const char *eigrp_if_name_string(struct eigrp_interface *); + +extern void eigrp_ip_header_dump(struct ip *); +extern void eigrp_header_dump(struct eigrp_header *); + +extern void show_ip_eigrp_interface_header(struct vty *, struct eigrp *); +extern void show_ip_eigrp_neighbor_header(struct vty *, struct eigrp *); +extern void show_ip_eigrp_topology_header(struct vty *, struct eigrp *); +extern void show_ip_eigrp_interface_detail(struct vty *, struct eigrp *, + struct eigrp_interface *); +extern void show_ip_eigrp_interface_sub(struct vty *, struct eigrp *, + struct eigrp_interface *); +extern void show_ip_eigrp_neighbor_sub(struct vty *, struct eigrp_neighbor *, + int); +extern void show_ip_eigrp_prefix_descriptor(struct vty *vty, + struct eigrp_prefix_descriptor *tn); +extern void show_ip_eigrp_route_descriptor(struct vty *vty, struct eigrp *eigrp, + struct eigrp_route_descriptor *ne, + bool *first); + +extern void eigrp_debug_init(void); + +#endif /* _ZEBRA_EIGRPD_DUMP_H_ */ diff --git a/eigrpd/eigrp_errors.c b/eigrpd/eigrp_errors.c new file mode 100644 index 0000000..9991fd5 --- /dev/null +++ b/eigrpd/eigrp_errors.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP-specific error messages. + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + */ + +#include <zebra.h> + +#include "lib/ferr.h" +#include "eigrp_errors.h" + +/* clang-format off */ +static struct log_ref ferr_eigrp_err[] = { + { + .code = EC_EIGRP_PACKET, + .title = "EIGRP Packet Error", + .description = "EIGRP has a packet that does not correctly decode or encode", + .suggestion = "Gather log files from both sides of the neighbor relationship and open an issue" + }, + { + .code = EC_EIGRP_CONFIG, + .title = "EIGRP Configuration Error", + .description = "EIGRP has detected a configuration error", + .suggestion = "Correct the configuration issue, if it still persists open an Issue" + }, + { + .code = END_FERR, + } +}; +/* clang-format on */ + +void eigrp_error_init(void) +{ + log_ref_add(ferr_eigrp_err); +} diff --git a/eigrpd/eigrp_errors.h b/eigrpd/eigrp_errors.h new file mode 100644 index 0000000..885702b --- /dev/null +++ b/eigrpd/eigrp_errors.h @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP-specific error messages. + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + */ + +#ifndef __EIGRP_ERRORS_H__ +#define __EIGRP_ERRORS_H__ + +#include "lib/ferr.h" + +enum eigrp_log_refs { + EC_EIGRP_PACKET = EIGRP_FERR_START, + EC_EIGRP_CONFIG, +}; + +extern void eigrp_error_init(void); + +#endif diff --git a/eigrpd/eigrp_filter.c b/eigrpd/eigrp_filter.c new file mode 100644 index 0000000..eceef6b --- /dev/null +++ b/eigrpd/eigrp_filter.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Filter Functions. + * Copyright (C) 2013-2015 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * Frantisek Gazo + * Tomas Hvorkovy + * Martin Kontsek + * Lukas Koribsky + * + */ + +#include <zebra.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" +#include "md5.h" +#include "keychain.h" +#include "privs.h" +#include "vrf.h" + +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_const.h" +#include "eigrpd/eigrp_filter.h" +#include "eigrpd/eigrp_packet.h" + +/* + * Distribute-list update functions. + */ +void eigrp_distribute_update(struct distribute_ctx *ctx, + struct distribute *dist) +{ + struct eigrp *e = eigrp_lookup(ctx->vrf->vrf_id); + struct interface *ifp; + struct eigrp_interface *ei = NULL; + struct access_list *alist; + struct prefix_list *plist; + // struct route_map *routemap; + + /* if no interface address is present, set list to eigrp process struct + */ + + /* Check if distribute-list was set for process or interface */ + if (!dist->ifname) { + /* access list IN for whole process */ + if (dist->list[DISTRIBUTE_V4_IN]) { + alist = access_list_lookup( + AFI_IP, dist->list[DISTRIBUTE_V4_IN]); + if (alist) + e->list[EIGRP_FILTER_IN] = alist; + else + e->list[EIGRP_FILTER_IN] = NULL; + } else { + e->list[EIGRP_FILTER_IN] = NULL; + } + + /* access list OUT for whole process */ + if (dist->list[DISTRIBUTE_V4_OUT]) { + alist = access_list_lookup( + AFI_IP, dist->list[DISTRIBUTE_V4_OUT]); + if (alist) + e->list[EIGRP_FILTER_OUT] = alist; + else + e->list[EIGRP_FILTER_OUT] = NULL; + } else { + e->list[EIGRP_FILTER_OUT] = NULL; + } + + /* PREFIX_LIST IN for process */ + if (dist->prefix[DISTRIBUTE_V4_IN]) { + plist = prefix_list_lookup( + AFI_IP, dist->prefix[DISTRIBUTE_V4_IN]); + if (plist) { + e->prefix[EIGRP_FILTER_IN] = plist; + } else + e->prefix[EIGRP_FILTER_IN] = NULL; + } else + e->prefix[EIGRP_FILTER_IN] = NULL; + + /* PREFIX_LIST OUT for process */ + if (dist->prefix[DISTRIBUTE_V4_OUT]) { + plist = prefix_list_lookup( + AFI_IP, dist->prefix[DISTRIBUTE_V4_OUT]); + if (plist) { + e->prefix[EIGRP_FILTER_OUT] = plist; + + } else + e->prefix[EIGRP_FILTER_OUT] = NULL; + } else + e->prefix[EIGRP_FILTER_OUT] = NULL; + + // TODO: check Graceful restart after 10sec + + /* cancel GR scheduled */ + event_cancel(&(e->t_distribute)); + + /* schedule Graceful restart for whole process in 10sec */ + event_add_timer(master, eigrp_distribute_timer_process, e, (10), + &e->t_distribute); + + return; + } + + ifp = if_lookup_by_name(dist->ifname, e->vrf_id); + if (ifp == NULL) + return; + + /*struct eigrp_if_info * info = ifp->info; + ei = info->eigrp_interface;*/ + struct listnode *node, *nnode; + struct eigrp_interface *ei2; + /* Find proper interface */ + for (ALL_LIST_ELEMENTS(e->eiflist, node, nnode, ei2)) { + if (strcmp(ei2->ifp->name, ifp->name) == 0) { + ei = ei2; + break; + } + } + assert(ei != NULL); + + /* Access-list for interface in */ + if (dist->list[DISTRIBUTE_V4_IN]) { + alist = access_list_lookup(AFI_IP, + dist->list[DISTRIBUTE_V4_IN]); + if (alist) { + ei->list[EIGRP_FILTER_IN] = alist; + } else + ei->list[EIGRP_FILTER_IN] = NULL; + } else { + ei->list[EIGRP_FILTER_IN] = NULL; + } + + /* Access-list for interface in */ + if (dist->list[DISTRIBUTE_V4_OUT]) { + alist = access_list_lookup(AFI_IP, + dist->list[DISTRIBUTE_V4_OUT]); + if (alist) + ei->list[EIGRP_FILTER_OUT] = alist; + else + ei->list[EIGRP_FILTER_OUT] = NULL; + + } else + ei->list[EIGRP_FILTER_OUT] = NULL; + + /* Prefix-list for interface in */ + if (dist->prefix[DISTRIBUTE_V4_IN]) { + plist = prefix_list_lookup(AFI_IP, + dist->prefix[DISTRIBUTE_V4_IN]); + if (plist) + ei->prefix[EIGRP_FILTER_IN] = plist; + else + ei->prefix[EIGRP_FILTER_IN] = NULL; + } else + ei->prefix[EIGRP_FILTER_IN] = NULL; + + /* Prefix-list for interface out */ + if (dist->prefix[DISTRIBUTE_V4_OUT]) { + plist = prefix_list_lookup(AFI_IP, + dist->prefix[DISTRIBUTE_V4_OUT]); + if (plist) + ei->prefix[EIGRP_FILTER_OUT] = plist; + else + ei->prefix[EIGRP_FILTER_OUT] = NULL; + } else + ei->prefix[EIGRP_FILTER_OUT] = NULL; + + // TODO: check Graceful restart after 10sec + + /* Cancel GR scheduled */ + event_cancel(&(ei->t_distribute)); + /* schedule Graceful restart for interface in 10sec */ + event_add_timer(master, eigrp_distribute_timer_interface, ei, 10, + &ei->t_distribute); +} + +/* + * Function called by prefix-list and access-list update + */ +void eigrp_distribute_update_interface(struct interface *ifp) +{ + struct distribute *dist; + struct eigrp *eigrp; + + eigrp = eigrp_lookup(ifp->vrf->vrf_id); + if (!eigrp) + return; + dist = distribute_lookup(eigrp->distribute_ctx, ifp->name); + if (dist) + eigrp_distribute_update(eigrp->distribute_ctx, + dist); +} + +/* Update all interface's distribute list. + * Function used in hook for prefix-list + */ +void eigrp_distribute_update_all(struct prefix_list *notused) +{ + struct vrf *vrf; + struct interface *ifp; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + FOR_ALL_INTERFACES (vrf, ifp) + eigrp_distribute_update_interface(ifp); + } +} + +/* + * Function used in hook for acces-list + */ +void eigrp_distribute_update_all_wrapper(struct access_list *notused) +{ + eigrp_distribute_update_all(NULL); +} + +/* + * @fn eigrp_distribute_timer_process + * + * @param[in] thread current execution thread timer is associated with + * + * @return void + * + * @par + * Called when 10sec waiting time expire and + * executes Graceful restart for whole process + */ +void eigrp_distribute_timer_process(struct event *thread) +{ + struct eigrp *eigrp; + + eigrp = EVENT_ARG(thread); + + /* execute GR for whole process */ + eigrp_update_send_process_GR(eigrp, EIGRP_GR_FILTER, NULL); +} + +/* + * @fn eigrp_distribute_timer_interface + * + * @param[in] thread current execution thread timer is associated with + * + * @return void + * + * @par + * Called when 10sec waiting time expire and + * executes Graceful restart for interface + */ +void eigrp_distribute_timer_interface(struct event *thread) +{ + struct eigrp_interface *ei; + + ei = EVENT_ARG(thread); + ei->t_distribute = NULL; + + /* execute GR for interface */ + eigrp_update_send_interface_GR(ei, EIGRP_GR_FILTER, NULL); +} diff --git a/eigrpd/eigrp_filter.h b/eigrpd/eigrp_filter.h new file mode 100644 index 0000000..dc8af8f --- /dev/null +++ b/eigrpd/eigrp_filter.h @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Filter Functions. + * Copyright (C) 2013-2016 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * Frantisek Gazo + * Tomas Hvorkovy + * Martin Kontsek + * Lukas Koribsky + * + */ + +#ifndef EIGRPD_EIGRP_FILTER_H_ +#define EIGRPD_EIGRP_FILTER_H_ + +extern void eigrp_distribute_update(struct distribute_ctx *ctx, + struct distribute *dist); +extern void eigrp_distribute_update_interface(struct interface *ifp); +extern void eigrp_distribute_update_all(struct prefix_list *plist); +extern void eigrp_distribute_update_all_wrapper(struct access_list *alist); +extern void eigrp_distribute_timer_process(struct event *thread); +extern void eigrp_distribute_timer_interface(struct event *thread); + +#endif /* EIGRPD_EIGRP_FILTER_H_ */ diff --git a/eigrpd/eigrp_fsm.c b/eigrpd/eigrp_fsm.c new file mode 100644 index 0000000..6d8061e --- /dev/null +++ b/eigrpd/eigrp_fsm.c @@ -0,0 +1,628 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRPd Finite State Machine (DUAL). + * Copyright (C) 2013-2014 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * + * This file contains functions for executing logic of finite state machine + * + * +------------ + + * | (7) | + * | v + * +=====================================+ + * | | + * | Passive | + * | | + * +=====================================+ + * ^ | ^ ^ ^ | + * (3)| | (1)| | (1)| | + * | (0)| | (3)| | (2)| + * | | | | | +---------------+ + * | | | | | \ + * +--------+ | | | +-----------------+ \ + * / / / | \ \ + * / / / +----+ \ \ + * | | | | | | + * | v | | | v + * +===========+ (6) +===========+ +===========+ (6) +===========+ + * | |------->| | (5) | |-------->| | + * | | (4) | |------>| | (4) | | + * | ACTIVE 0 |<-------| ACTIVE 1 | | ACTIVE 2 |<--------| ACTIVE 3 + * | + * +--| | +--| | +--| | +--| | + * | +===========+ | +===========+ | +===========+ | + * +===========+ + * | ^ |(5) | ^ | ^ ^ | ^ + * | | +---------|------|------------|----+ | | | + * +-------+ +------+ +---------+ +---------+ + * (7) (7) (7) (7) + * + * 0- input event other than query from successor, FC not satisfied + * 1- last reply, FD is reset + * 2- query from successor, FC not satisfied + * 3- last reply, FC satisfied with current value of FDij + * 4- distance increase while in active state + * 5- query from successor while in active state + * 6- last reply, FC not satisfied with current value of FDij + * 7- state not changed, usually by receiving not last reply + */ + +#include <zebra.h> + +#include "frrevent.h" +#include "prefix.h" +#include "table.h" +#include "memory.h" +#include "log.h" +#include "linklist.h" +#include "vty.h" + +#include "eigrpd/eigrp_types.h" +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_interface.h" +#include "eigrpd/eigrp_neighbor.h" +#include "eigrpd/eigrp_packet.h" +#include "eigrpd/eigrp_zebra.h" +#include "eigrpd/eigrp_vty.h" +#include "eigrpd/eigrp_network.h" +#include "eigrpd/eigrp_dump.h" +#include "eigrpd/eigrp_topology.h" +#include "eigrpd/eigrp_fsm.h" +#include "eigrpd/eigrp_metric.h" + +/* + * Prototypes + */ +int eigrp_fsm_event_keep_state(struct eigrp_fsm_action_message *); +int eigrp_fsm_event_nq_fcn(struct eigrp_fsm_action_message *); +int eigrp_fsm_event_q_fcn(struct eigrp_fsm_action_message *); +int eigrp_fsm_event_lr(struct eigrp_fsm_action_message *); +int eigrp_fsm_event_dinc(struct eigrp_fsm_action_message *); +int eigrp_fsm_event_lr_fcs(struct eigrp_fsm_action_message *); +int eigrp_fsm_event_lr_fcn(struct eigrp_fsm_action_message *); +int eigrp_fsm_event_qact(struct eigrp_fsm_action_message *); + +//--------------------------------------------------------------------- + +/* + * NSM - field of fields of struct containing one function each. + * Which function is used depends on actual state of FSM and occurred + * event(arrow in diagram). Usage: + * NSM[actual/starting state][occurred event].func + * Functions are should be executed within separate thread. + */ +const struct { + int (*func)(struct eigrp_fsm_action_message *); +} NSM[EIGRP_FSM_STATE_MAX][EIGRP_FSM_EVENT_MAX] = { + { + // PASSIVE STATE + {eigrp_fsm_event_nq_fcn}, /* Event 0 */ + {eigrp_fsm_event_keep_state}, /* Event 1 */ + {eigrp_fsm_event_q_fcn}, /* Event 2 */ + {eigrp_fsm_event_keep_state}, /* Event 3 */ + {eigrp_fsm_event_keep_state}, /* Event 4 */ + {eigrp_fsm_event_keep_state}, /* Event 5 */ + {eigrp_fsm_event_keep_state}, /* Event 6 */ + {eigrp_fsm_event_keep_state}, /* Event 7 */ + }, + { + // Active 0 state + {eigrp_fsm_event_keep_state}, /* Event 0 */ + {eigrp_fsm_event_keep_state}, /* Event 1 */ + {eigrp_fsm_event_keep_state}, /* Event 2 */ + {eigrp_fsm_event_lr_fcs}, /* Event 3 */ + {eigrp_fsm_event_keep_state}, /* Event 4 */ + {eigrp_fsm_event_qact}, /* Event 5 */ + {eigrp_fsm_event_lr_fcn}, /* Event 6 */ + {eigrp_fsm_event_keep_state}, /* Event 7 */ + }, + { + // Active 1 state + {eigrp_fsm_event_keep_state}, /* Event 0 */ + {eigrp_fsm_event_lr}, /* Event 1 */ + {eigrp_fsm_event_keep_state}, /* Event 2 */ + {eigrp_fsm_event_keep_state}, /* Event 3 */ + {eigrp_fsm_event_dinc}, /* Event 4 */ + {eigrp_fsm_event_qact}, /* Event 5 */ + {eigrp_fsm_event_keep_state}, /* Event 6 */ + {eigrp_fsm_event_keep_state}, /* Event 7 */ + }, + { + // Active 2 state + {eigrp_fsm_event_keep_state}, /* Event 0 */ + {eigrp_fsm_event_keep_state}, /* Event 1 */ + {eigrp_fsm_event_keep_state}, /* Event 2 */ + {eigrp_fsm_event_lr_fcs}, /* Event 3 */ + {eigrp_fsm_event_keep_state}, /* Event 4 */ + {eigrp_fsm_event_keep_state}, /* Event 5 */ + {eigrp_fsm_event_lr_fcn}, /* Event 6 */ + {eigrp_fsm_event_keep_state}, /* Event 7 */ + }, + { + // Active 3 state + {eigrp_fsm_event_keep_state}, /* Event 0 */ + {eigrp_fsm_event_lr}, /* Event 1 */ + {eigrp_fsm_event_keep_state}, /* Event 2 */ + {eigrp_fsm_event_keep_state}, /* Event 3 */ + {eigrp_fsm_event_dinc}, /* Event 4 */ + {eigrp_fsm_event_keep_state}, /* Event 5 */ + {eigrp_fsm_event_keep_state}, /* Event 6 */ + {eigrp_fsm_event_keep_state}, /* Event 7 */ + }, +}; + +static const char *packet_type2str(uint8_t packet_type) +{ + if (packet_type == EIGRP_OPC_UPDATE) + return "Update"; + if (packet_type == EIGRP_OPC_REQUEST) + return "Request"; + if (packet_type == EIGRP_OPC_QUERY) + return "Query"; + if (packet_type == EIGRP_OPC_REPLY) + return "Reply"; + if (packet_type == EIGRP_OPC_HELLO) + return "Hello"; + if (packet_type == EIGRP_OPC_IPXSAP) + return "IPXSAP"; + if (packet_type == EIGRP_OPC_ACK) + return "Ack"; + if (packet_type == EIGRP_OPC_SIAQUERY) + return "SIA Query"; + if (packet_type == EIGRP_OPC_SIAREPLY) + return "SIA Reply"; + + return "Unknown"; +} + +static const char *prefix_state2str(enum eigrp_fsm_states state) +{ + switch (state) { + case EIGRP_FSM_STATE_PASSIVE: + return "Passive"; + case EIGRP_FSM_STATE_ACTIVE_0: + return "Active oij0"; + case EIGRP_FSM_STATE_ACTIVE_1: + return "Active oij1"; + case EIGRP_FSM_STATE_ACTIVE_2: + return "Active oij2"; + case EIGRP_FSM_STATE_ACTIVE_3: + return "Active oij3"; + } + + return "Unknown"; +} + +static const char *fsm_state2str(enum eigrp_fsm_events event) +{ + switch (event) { + case EIGRP_FSM_KEEP_STATE: + return "Keep State Event"; + case EIGRP_FSM_EVENT_NQ_FCN: + return "Non Query Event Feasability not satisfied"; + case EIGRP_FSM_EVENT_LR: + return "Last Reply Event"; + case EIGRP_FSM_EVENT_Q_FCN: + return "Query Event Feasability not satisfied"; + case EIGRP_FSM_EVENT_LR_FCS: + return "Last Reply Event Feasability satisfied"; + case EIGRP_FSM_EVENT_DINC: + return "Distance Increase Event"; + case EIGRP_FSM_EVENT_QACT: + return "Query from Successor while in active state"; + case EIGRP_FSM_EVENT_LR_FCN: + return "Last Reply Event, Feasibility not satisfied"; + } + + return "Unknown"; +} + +static const char *change2str(enum metric_change change) +{ + switch (change) { + case METRIC_DECREASE: + return "Decrease"; + case METRIC_SAME: + return "Same"; + case METRIC_INCREASE: + return "Increase"; + } + + return "Unknown"; +} +/* + * Main function in which are make decisions which event occurred. + * msg - argument of type struct eigrp_fsm_action_message contain + * details about what happen + * + * Return number of occurred event (arrow in diagram). + * + */ +static enum eigrp_fsm_events +eigrp_get_fsm_event(struct eigrp_fsm_action_message *msg) +{ + // Loading base information from message + // struct eigrp *eigrp = msg->eigrp; + struct eigrp_prefix_descriptor *prefix = msg->prefix; + struct eigrp_route_descriptor *entry = msg->entry; + uint8_t actual_state = prefix->state; + enum metric_change change; + + if (entry == NULL) { + entry = eigrp_route_descriptor_new(); + entry->adv_router = msg->adv_router; + entry->ei = msg->adv_router->ei; + entry->prefix = prefix; + msg->entry = entry; + } + + /* + * Calculate resultant metrics and insert to correct position + * in entries list + */ + change = eigrp_topology_update_distance(msg); + + /* Store for display later */ + msg->change = change; + + switch (actual_state) { + case EIGRP_FSM_STATE_PASSIVE: { + struct eigrp_route_descriptor *head = + listnode_head(prefix->entries); + + if (head->reported_distance < prefix->fdistance) { + return EIGRP_FSM_KEEP_STATE; + } + /* + * if best entry doesn't satisfy feasibility condition it means + * move to active state + * dependently if it was query from successor + */ + if (msg->packet_type == EIGRP_OPC_QUERY) { + return EIGRP_FSM_EVENT_Q_FCN; + } else { + return EIGRP_FSM_EVENT_NQ_FCN; + } + + break; + } + case EIGRP_FSM_STATE_ACTIVE_0: { + if (msg->packet_type == EIGRP_OPC_REPLY) { + struct eigrp_route_descriptor *head = + listnode_head(prefix->entries); + + listnode_delete(prefix->rij, entry->adv_router); + if (prefix->rij->count) + return EIGRP_FSM_KEEP_STATE; + + zlog_info("All reply received"); + if (head->reported_distance < prefix->fdistance) { + return EIGRP_FSM_EVENT_LR_FCS; + } + + return EIGRP_FSM_EVENT_LR_FCN; + } else if (msg->packet_type == EIGRP_OPC_QUERY + && (entry->flags + & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG)) { + return EIGRP_FSM_EVENT_QACT; + } + + return EIGRP_FSM_KEEP_STATE; + + break; + } + case EIGRP_FSM_STATE_ACTIVE_1: { + if (msg->packet_type == EIGRP_OPC_QUERY + && (entry->flags & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG)) { + return EIGRP_FSM_EVENT_QACT; + } else if (msg->packet_type == EIGRP_OPC_REPLY) { + listnode_delete(prefix->rij, entry->adv_router); + + if (change == METRIC_INCREASE + && (entry->flags + & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG)) { + return EIGRP_FSM_EVENT_DINC; + } else if (prefix->rij->count) { + return EIGRP_FSM_KEEP_STATE; + } else { + zlog_info("All reply received"); + return EIGRP_FSM_EVENT_LR; + } + } else if (msg->packet_type == EIGRP_OPC_UPDATE + && change == METRIC_INCREASE + && (entry->flags + & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG)) { + return EIGRP_FSM_EVENT_DINC; + } + return EIGRP_FSM_KEEP_STATE; + + break; + } + case EIGRP_FSM_STATE_ACTIVE_2: { + if (msg->packet_type == EIGRP_OPC_REPLY) { + struct eigrp_route_descriptor *head = + listnode_head(prefix->entries); + + listnode_delete(prefix->rij, entry->adv_router); + if (prefix->rij->count) { + return EIGRP_FSM_KEEP_STATE; + } else { + zlog_info("All reply received"); + if (head->reported_distance + < prefix->fdistance) { + return EIGRP_FSM_EVENT_LR_FCS; + } + + return EIGRP_FSM_EVENT_LR_FCN; + } + } + return EIGRP_FSM_KEEP_STATE; + + break; + } + case EIGRP_FSM_STATE_ACTIVE_3: { + if (msg->packet_type == EIGRP_OPC_REPLY) { + listnode_delete(prefix->rij, entry->adv_router); + + if (change == METRIC_INCREASE + && (entry->flags + & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG)) { + return EIGRP_FSM_EVENT_DINC; + } else if (prefix->rij->count) { + return EIGRP_FSM_KEEP_STATE; + } else { + zlog_info("All reply received"); + return EIGRP_FSM_EVENT_LR; + } + } else if (msg->packet_type == EIGRP_OPC_UPDATE + && change == METRIC_INCREASE + && (entry->flags + & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG)) { + return EIGRP_FSM_EVENT_DINC; + } + return EIGRP_FSM_KEEP_STATE; + + break; + } + } + + return EIGRP_FSM_KEEP_STATE; +} + +/* + * Function made to execute in separate thread. + * Load argument from thread and execute proper NSM function + */ +int eigrp_fsm_event(struct eigrp_fsm_action_message *msg) +{ + enum eigrp_fsm_events event = eigrp_get_fsm_event(msg); + + zlog_info( + "EIGRP AS: %d State: %s Event: %s Network: %pI4 Packet Type: %s Reply RIJ Count: %d change: %s", + msg->eigrp->AS, prefix_state2str(msg->prefix->state), + fsm_state2str(event), &msg->prefix->destination->u.prefix4, + packet_type2str(msg->packet_type), msg->prefix->rij->count, + change2str(msg->change)); + (*(NSM[msg->prefix->state][event].func))(msg); + + return 1; +} + +/* + * Function of event 0. + * + */ +int eigrp_fsm_event_nq_fcn(struct eigrp_fsm_action_message *msg) +{ + struct eigrp *eigrp = msg->eigrp; + struct eigrp_prefix_descriptor *prefix = msg->prefix; + struct list *successors = eigrp_topology_get_successor(prefix); + struct eigrp_route_descriptor *ne; + + assert(successors); // If this is NULL we have shit the bed, fun huh? + + ne = listnode_head(successors); + prefix->state = EIGRP_FSM_STATE_ACTIVE_1; + prefix->rdistance = prefix->distance = prefix->fdistance = ne->distance; + prefix->reported_metric = ne->total_metric; + + if (eigrp_nbr_count_get(eigrp)) { + prefix->req_action |= EIGRP_FSM_NEED_QUERY; + listnode_add(eigrp->topology_changes_internalIPV4, prefix); + } else { + eigrp_fsm_event_lr(msg); // in the case that there are no more + // neighbors left + } + + list_delete(&successors); + + return 1; +} + +int eigrp_fsm_event_q_fcn(struct eigrp_fsm_action_message *msg) +{ + struct eigrp *eigrp = msg->eigrp; + struct eigrp_prefix_descriptor *prefix = msg->prefix; + struct list *successors = eigrp_topology_get_successor(prefix); + struct eigrp_route_descriptor *ne; + + assert(successors); // If this is NULL somebody poked us in the eye. + + ne = listnode_head(successors); + prefix->state = EIGRP_FSM_STATE_ACTIVE_3; + prefix->rdistance = prefix->distance = prefix->fdistance = ne->distance; + prefix->reported_metric = ne->total_metric; + if (eigrp_nbr_count_get(eigrp)) { + prefix->req_action |= EIGRP_FSM_NEED_QUERY; + listnode_add(eigrp->topology_changes_internalIPV4, prefix); + } else { + eigrp_fsm_event_lr(msg); // in the case that there are no more + // neighbors left + } + + list_delete(&successors); + + return 1; +} + +int eigrp_fsm_event_keep_state(struct eigrp_fsm_action_message *msg) +{ + struct eigrp *eigrp = msg->eigrp; + struct eigrp_prefix_descriptor *prefix = msg->prefix; + struct eigrp_route_descriptor *ne = listnode_head(prefix->entries); + + if (prefix->state == EIGRP_FSM_STATE_PASSIVE) { + if (!eigrp_metrics_is_same(prefix->reported_metric, + ne->total_metric)) { + prefix->rdistance = prefix->fdistance = + prefix->distance = ne->distance; + prefix->reported_metric = ne->total_metric; + if (msg->packet_type == EIGRP_OPC_QUERY) + eigrp_send_reply(msg->adv_router, prefix); + prefix->req_action |= EIGRP_FSM_NEED_UPDATE; + listnode_add(eigrp->topology_changes_internalIPV4, + prefix); + } + eigrp_topology_update_node_flags(eigrp, prefix); + eigrp_update_routing_table(eigrp, prefix); + } + + if (msg->packet_type == EIGRP_OPC_QUERY) + eigrp_send_reply(msg->adv_router, prefix); + + return 1; +} + +int eigrp_fsm_event_lr(struct eigrp_fsm_action_message *msg) +{ + struct eigrp *eigrp = msg->eigrp; + struct eigrp_prefix_descriptor *prefix = msg->prefix; + struct eigrp_route_descriptor *ne = listnode_head(prefix->entries); + + prefix->fdistance = prefix->distance = prefix->rdistance = ne->distance; + prefix->reported_metric = ne->total_metric; + + if (prefix->state == EIGRP_FSM_STATE_ACTIVE_3) { + struct list *successors = eigrp_topology_get_successor(prefix); + + assert(successors); // It's like Napolean and Waterloo + + ne = listnode_head(successors); + eigrp_send_reply(ne->adv_router, prefix); + list_delete(&successors); + } + + prefix->state = EIGRP_FSM_STATE_PASSIVE; + prefix->req_action |= EIGRP_FSM_NEED_UPDATE; + listnode_add(eigrp->topology_changes_internalIPV4, prefix); + eigrp_topology_update_node_flags(eigrp, prefix); + eigrp_update_routing_table(eigrp, prefix); + eigrp_update_topology_table_prefix(eigrp, eigrp->topology_table, + prefix); + + return 1; +} + +int eigrp_fsm_event_dinc(struct eigrp_fsm_action_message *msg) +{ + struct list *successors = eigrp_topology_get_successor(msg->prefix); + struct eigrp_route_descriptor *ne; + + assert(successors); // Trump and his big hands + + ne = listnode_head(successors); + msg->prefix->state = msg->prefix->state == EIGRP_FSM_STATE_ACTIVE_1 + ? EIGRP_FSM_STATE_ACTIVE_0 + : EIGRP_FSM_STATE_ACTIVE_2; + msg->prefix->distance = ne->distance; + if (!msg->prefix->rij->count) + (*(NSM[msg->prefix->state][eigrp_get_fsm_event(msg)].func))( + msg); + + + list_delete(&successors); + return 1; +} + +int eigrp_fsm_event_lr_fcs(struct eigrp_fsm_action_message *msg) +{ + struct eigrp *eigrp = msg->eigrp; + struct eigrp_prefix_descriptor *prefix = msg->prefix; + struct eigrp_route_descriptor *ne = listnode_head(prefix->entries); + + prefix->state = EIGRP_FSM_STATE_PASSIVE; + prefix->distance = prefix->rdistance = ne->distance; + prefix->reported_metric = ne->total_metric; + prefix->fdistance = prefix->fdistance > prefix->distance + ? prefix->distance + : prefix->fdistance; + if (prefix->state == EIGRP_FSM_STATE_ACTIVE_2) { + struct list *successors = eigrp_topology_get_successor(prefix); + + assert(successors); // Having a spoon and all you need is a + // knife + ne = listnode_head(successors); + eigrp_send_reply(ne->adv_router, prefix); + + list_delete(&successors); + } + prefix->req_action |= EIGRP_FSM_NEED_UPDATE; + listnode_add(eigrp->topology_changes_internalIPV4, prefix); + eigrp_topology_update_node_flags(eigrp, prefix); + eigrp_update_routing_table(eigrp, prefix); + eigrp_update_topology_table_prefix(eigrp, eigrp->topology_table, + prefix); + + return 1; +} + +int eigrp_fsm_event_lr_fcn(struct eigrp_fsm_action_message *msg) +{ + struct eigrp *eigrp = msg->eigrp; + struct eigrp_prefix_descriptor *prefix = msg->prefix; + struct eigrp_route_descriptor *best_successor; + struct list *successors = eigrp_topology_get_successor(prefix); + + assert(successors); // Routing without a stack + + prefix->state = prefix->state == EIGRP_FSM_STATE_ACTIVE_0 + ? EIGRP_FSM_STATE_ACTIVE_1 + : EIGRP_FSM_STATE_ACTIVE_3; + + best_successor = listnode_head(successors); + prefix->rdistance = prefix->distance = best_successor->distance; + prefix->reported_metric = best_successor->total_metric; + + if (eigrp_nbr_count_get(eigrp)) { + prefix->req_action |= EIGRP_FSM_NEED_QUERY; + listnode_add(eigrp->topology_changes_internalIPV4, prefix); + } else { + eigrp_fsm_event_lr(msg); // in the case that there are no more + // neighbors left + } + + list_delete(&successors); + + return 1; +} + +int eigrp_fsm_event_qact(struct eigrp_fsm_action_message *msg) +{ + struct list *successors = eigrp_topology_get_successor(msg->prefix); + struct eigrp_route_descriptor *ne; + + assert(successors); // Cats and no Dogs + + ne = listnode_head(successors); + msg->prefix->state = EIGRP_FSM_STATE_ACTIVE_2; + msg->prefix->distance = ne->distance; + + list_delete(&successors); + return 1; +} diff --git a/eigrpd/eigrp_fsm.h b/eigrpd/eigrp_fsm.h new file mode 100644 index 0000000..e9c5223 --- /dev/null +++ b/eigrpd/eigrp_fsm.h @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Finite State Machine (DUAL). + * Copyright (C) 2013-2014 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + */ + +#ifndef _ZEBRA_EIGRP_FSM_H +#define _ZEBRA_EIGRP_FSM_H + +extern int eigrp_fsm_event(struct eigrp_fsm_action_message *msg); + + +#endif /* _ZEBRA_EIGRP_DUAL_H */ diff --git a/eigrpd/eigrp_hello.c b/eigrpd/eigrp_hello.c new file mode 100644 index 0000000..ee0e245 --- /dev/null +++ b/eigrpd/eigrp_hello.c @@ -0,0 +1,783 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Sending and Receiving EIGRP Hello Packets. + * Copyright (C) 2013-2016 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * Frantisek Gazo + * Tomas Hvorkovy + * Martin Kontsek + * Lukas Koribsky + */ + +#include <zebra.h> + +#include "frrevent.h" +#include "memory.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "sockunion.h" +#include "stream.h" +#include "log.h" +#include "sockopt.h" +#include "checksum.h" +#include "vty.h" +#include "md5.h" + +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_interface.h" +#include "eigrpd/eigrp_neighbor.h" +#include "eigrpd/eigrp_packet.h" +#include "eigrpd/eigrp_zebra.h" +#include "eigrpd/eigrp_vty.h" +#include "eigrpd/eigrp_dump.h" +#include "eigrpd/eigrp_macros.h" +#include "eigrpd/eigrp_errors.h" + +/* Packet Type String. */ +static const struct message eigrp_general_tlv_type_str[] = { + {EIGRP_TLV_PARAMETER, "PARAMETER"}, + {EIGRP_TLV_AUTH, "AUTH"}, + {EIGRP_TLV_SEQ, "SEQ"}, + {EIGRP_TLV_SW_VERSION, "SW_VERSION"}, + {EIGRP_TLV_NEXT_MCAST_SEQ, "NEXT_MCAST_SEQ"}, + {EIGRP_TLV_PEER_TERMINATION, "PEER_TERMINATION"}, + {EIGRP_TLV_PEER_MTRLIST, "PEER_MTRLIST"}, + {EIGRP_TLV_PEER_TIDLIST, "PEER_TIDLIST"}, + {0}}; + + +/* + * @fn eigrp_hello_timer + * + * @param[in] thread current execution thread timer is associated with + * + * @return void + * + * @par + * Called once per "hello" time interval, default 5 seconds + * Sends hello packet via multicast for all interfaces eigrp + * is configured for + */ +void eigrp_hello_timer(struct event *thread) +{ + struct eigrp_interface *ei; + + ei = EVENT_ARG(thread); + + if (IS_DEBUG_EIGRP(0, TIMERS)) + zlog_debug("Start Hello Timer (%s) Expire [%u]", IF_NAME(ei), + ei->params.v_hello); + + /* Sending hello packet. */ + eigrp_hello_send(ei, EIGRP_HELLO_NORMAL, NULL); + + /* Hello timer set. */ + event_add_timer(master, eigrp_hello_timer, ei, ei->params.v_hello, + &ei->t_hello); +} + +/** + * @fn eigrp_hello_parameter_decode + * + * @param[in] nbr neighbor the ACK should be sent to + * @param[in] param pointer packet TLV is stored to + * + * @return uint16_t number of bytes added to packet stream + * + * @par + * Encode Parameter TLV, used to convey metric weights and the hold time. + * + * @usage + * Note the addition of K6 for the new extended metrics, and does not apply to + * older TLV packet formats. + */ +static struct eigrp_neighbor * +eigrp_hello_parameter_decode(struct eigrp_neighbor *nbr, + struct eigrp_tlv_hdr_type *tlv) +{ + struct eigrp *eigrp = nbr->ei->eigrp; + struct TLV_Parameter_Type *param = (struct TLV_Parameter_Type *)tlv; + + /* First validate TLV length */ + if (tlv->length < sizeof(struct TLV_Parameter_Type)) + return NULL; + + /* copy over the values passed in by the neighbor */ + nbr->K1 = param->K1; + nbr->K2 = param->K2; + nbr->K3 = param->K3; + nbr->K4 = param->K4; + nbr->K5 = param->K5; + nbr->K6 = param->K6; + nbr->v_holddown = ntohs(param->hold_time); + + /* + * Check K1-K5 have the correct values to be able to become neighbors + * K6 does not have to match + */ + if ((eigrp->k_values[0] == nbr->K1) && (eigrp->k_values[1] == nbr->K2) + && (eigrp->k_values[2] == nbr->K3) + && (eigrp->k_values[3] == nbr->K4) + && (eigrp->k_values[4] == nbr->K5)) { + + if (eigrp_nbr_state_get(nbr) == EIGRP_NEIGHBOR_DOWN) { + zlog_info( + "Neighbor %pI4 (%s) is pending: new adjacency", + &nbr->src, + ifindex2ifname(nbr->ei->ifp->ifindex, + eigrp->vrf_id)); + + /* Expedited hello sent */ + eigrp_hello_send(nbr->ei, EIGRP_HELLO_NORMAL, NULL); + + // if(ntohl(nbr->ei->address->u.prefix4.s_addr) > + // ntohl(nbr->src.s_addr)) + eigrp_update_send_init(nbr); + + eigrp_nbr_state_set(nbr, EIGRP_NEIGHBOR_PENDING); + } + } else { + if (eigrp_nbr_state_get(nbr) != EIGRP_NEIGHBOR_DOWN) { + if ((param->K1 & param->K2 & param->K3 & param->K4 + & param->K5) + == 255) { + zlog_info( + "Neighbor %pI4 (%s) is down: Interface PEER-TERMINATION received", + &nbr->src, + ifindex2ifname(nbr->ei->ifp->ifindex, + eigrp->vrf_id)); + eigrp_nbr_delete(nbr); + return NULL; + } else { + zlog_info( + "Neighbor %pI4 (%s) going down: Kvalue mismatch", + &nbr->src, + ifindex2ifname(nbr->ei->ifp->ifindex, + eigrp->vrf_id)); + eigrp_nbr_state_set(nbr, EIGRP_NEIGHBOR_DOWN); + } + } + } + + return nbr; +} + +static uint8_t +eigrp_hello_authentication_decode(struct stream *s, + struct eigrp_tlv_hdr_type *tlv_header, + struct eigrp_neighbor *nbr) +{ + struct TLV_MD5_Authentication_Type *md5; + + md5 = (struct TLV_MD5_Authentication_Type *)tlv_header; + + if (md5->auth_type == EIGRP_AUTH_TYPE_MD5) { + /* Validate tlv length */ + if (md5->length < sizeof(struct TLV_MD5_Authentication_Type)) + return 0; + + return eigrp_check_md5_digest(s, md5, nbr, + EIGRP_AUTH_BASIC_HELLO_FLAG); + } else if (md5->auth_type == EIGRP_AUTH_TYPE_SHA256) { + /* Validate tlv length */ + if (md5->length < sizeof(struct TLV_SHA256_Authentication_Type)) + return 0; + + return eigrp_check_sha256_digest( + s, (struct TLV_SHA256_Authentication_Type *)tlv_header, + nbr, EIGRP_AUTH_BASIC_HELLO_FLAG); + } + + return 0; +} + +/** + * @fn eigrp_sw_version_decode + * + * @param[in] nbr neighbor the ACK shoudl be sent to + * @param[in] param pointer to TLV software version information + * + * @return void + * + * @par + * Read the software version in the specified location. + * This consists of two bytes of OS version, and two bytes of EIGRP + * revision number. + */ +static void eigrp_sw_version_decode(struct eigrp_neighbor *nbr, + struct eigrp_tlv_hdr_type *tlv) +{ + struct TLV_Software_Type *version = (struct TLV_Software_Type *)tlv; + + /* Validate TLV length */ + if (tlv->length < sizeof(struct TLV_Software_Type)) + return; + + nbr->os_rel_major = version->vender_major; + nbr->os_rel_minor = version->vender_minor; + nbr->tlv_rel_major = version->eigrp_major; + nbr->tlv_rel_minor = version->eigrp_minor; + return; +} + +/** + * @fn eigrp_peer_termination_decode + * + * @param[in] nbr neighbor the ACK shoudl be sent to + * @param[in] tlv pointer to TLV software version information + * + * @return void + * + * @par + * Read the address in the TLV and match to out address. If + * a match is found, move the sending neighbor to the down state. If + * out address is not in the TLV, then ignore the peer termination + */ +static void eigrp_peer_termination_decode(struct eigrp_neighbor *nbr, + struct eigrp_tlv_hdr_type *tlv) +{ + struct eigrp *eigrp = nbr->ei->eigrp; + struct TLV_Peer_Termination_type *param = + (struct TLV_Peer_Termination_type *)tlv; + + /* Validate TLV length */ + if (tlv->length < sizeof(struct TLV_Peer_Termination_type)) + return; + + uint32_t my_ip = nbr->ei->address.u.prefix4.s_addr; + uint32_t received_ip = param->neighbor_ip; + + if (my_ip == received_ip) { + zlog_info( + "Neighbor %pI4 (%s) is down: Peer Termination received", + &nbr->src, + ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id)); + /* set neighbor to DOWN */ + nbr->state = EIGRP_NEIGHBOR_DOWN; + /* delete neighbor */ + eigrp_nbr_delete(nbr); + } +} + +/** + * @fn eigrp_peer_termination_encode + * + * @param[in,out] s packet stream TLV is stored to + * @param[in] nbr_addr pointer to neighbor address for Peer + * Termination TLV + * + * @return uint16_t number of bytes added to packet stream + * + * @par + * Function used to encode Peer Termination TLV to Hello packet. + */ +static uint16_t eigrp_peer_termination_encode(struct stream *s, + struct in_addr *nbr_addr) +{ + uint16_t length = EIGRP_TLV_PEER_TERMINATION_LEN; + + /* fill in type and length */ + stream_putw(s, EIGRP_TLV_PEER_TERMINATION); + stream_putw(s, length); + + /* fill in unknown field 0x04 */ + stream_putc(s, 0x04); + + /* finally neighbor IP address */ + stream_put_ipv4(s, nbr_addr->s_addr); + + return (length); +} + +/* + * @fn eigrp_hello_receive + * + * @param[in] eigrp eigrp routing process + * @param[in] iph pointer to ip header + * @param[in] eigrph pointer to eigrp header + * @param[in] s input ip stream + * @param[in] ei eigrp interface packet arrived on + * @param[in] size size of eigrp packet + * + * @return void + * + * @par + * This is the main worker function for processing hello packets. It + * will validate the peer associated with the src ip address of the ip + * header, and then decode each of the general TLVs which the packet + * may contain. + * + * @usage + * Not all TLVs are current decoder. This is a work in progress.. + */ +void eigrp_hello_receive(struct eigrp *eigrp, struct ip *iph, + struct eigrp_header *eigrph, struct stream *s, + struct eigrp_interface *ei, int size) +{ + struct eigrp_tlv_hdr_type *tlv_header; + struct eigrp_neighbor *nbr; + uint16_t type; + uint16_t length; + + /* get neighbor struct */ + nbr = eigrp_nbr_get(ei, eigrph, iph); + + /* neighbor must be valid, eigrp_nbr_get creates if none existed */ + assert(nbr); + + if (IS_DEBUG_EIGRP_PACKET(eigrph->opcode - 1, RECV)) + zlog_debug("Processing Hello size[%u] int(%s) nbr(%pI4)", size, + ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id), + &nbr->src); + + size -= EIGRP_HEADER_LEN; + if (size < 0) + return; + + tlv_header = (struct eigrp_tlv_hdr_type *)eigrph->tlv; + + do { + type = ntohs(tlv_header->type); + length = ntohs(tlv_header->length); + + /* Validate length against packet size */ + if (length > size) + return; + + if ((length > 0) && (length <= size)) { + if (IS_DEBUG_EIGRP_PACKET(0, RECV)) + zlog_debug( + " General TLV(%s)", + lookup_msg(eigrp_general_tlv_type_str, + type, NULL)); + + // determine what General TLV is being processed + switch (type) { + case EIGRP_TLV_PARAMETER: + nbr = eigrp_hello_parameter_decode(nbr, + tlv_header); + if (!nbr) + return; + break; + case EIGRP_TLV_AUTH: { + if (eigrp_hello_authentication_decode( + s, tlv_header, nbr) + == 0) + return; + else + break; + break; + } + case EIGRP_TLV_SEQ: + break; + case EIGRP_TLV_SW_VERSION: + eigrp_sw_version_decode(nbr, tlv_header); + break; + case EIGRP_TLV_NEXT_MCAST_SEQ: + break; + case EIGRP_TLV_PEER_TERMINATION: + eigrp_peer_termination_decode(nbr, tlv_header); + return; + break; + case EIGRP_TLV_PEER_MTRLIST: + case EIGRP_TLV_PEER_TIDLIST: + break; + default: + break; + } + } + + tlv_header = (struct eigrp_tlv_hdr_type *)(((char *)tlv_header) + + length); + size -= length; + + } while (size > 0); + + + /*If received packet is hello with Parameter TLV*/ + if (ntohl(eigrph->ack) == 0) { + /* increment statistics. */ + ei->hello_in++; + if (nbr) + eigrp_nbr_state_update(nbr); + } + + if (IS_DEBUG_EIGRP_PACKET(0, RECV)) + zlog_debug("Hello Packet received from %pI4", &nbr->src); +} + +uint32_t FRR_MAJOR; +uint32_t FRR_MINOR; + +void eigrp_sw_version_initialize(void) +{ + char ver_string[] = VERSION; + char *dash = strstr(ver_string, "-"); + int ret; + + if (dash) + dash[0] = '\0'; + + ret = sscanf(ver_string, "%" SCNu32 ".%" SCNu32, &FRR_MAJOR, + &FRR_MINOR); + if (ret != 2) + flog_err(EC_EIGRP_PACKET, + "Did not Properly parse %s, please fix VERSION string", + VERSION); +} + +/** + * @fn eigrp_sw_version_encode + * + * @param[in,out] s packet stream TLV is stored to + * + * @return uint16_t number of bytes added to packet stream + * + * @par + * Store the software version in the specified location. + * This consists of two bytes of OS version, and two bytes of EIGRP + * revision number. + */ +static uint16_t eigrp_sw_version_encode(struct stream *s) +{ + uint16_t length = EIGRP_TLV_SW_VERSION_LEN; + + // setup the tlv fields + stream_putw(s, EIGRP_TLV_SW_VERSION); + stream_putw(s, length); + + stream_putc(s, FRR_MAJOR); //!< major os version + stream_putc(s, FRR_MINOR); //!< minor os version + + /* and the core eigrp version */ + stream_putc(s, EIGRP_MAJOR_VERSION); + stream_putc(s, EIGRP_MINOR_VERSION); + + return (length); +} + +/** + * @fn eigrp_tidlist_encode + * + * @param[in,out] s packet stream TLV is stored to + * + * @return void + * + * @par + * If doing mutli-topology, then store the supported TID list. + * This is currently a place holder function + */ +static uint16_t eigrp_tidlist_encode(struct stream *s) +{ + // uint16_t length = EIGRP_TLV_SW_VERSION_LEN; + return 0; +} + +/** + * @fn eigrp_sequence_encode + * + * @param[in,out] s packet stream TLV is stored to + * + * @return uint16_t number of bytes added to packet stream + * + * @par + * Part of conditional receive process + * + */ +static uint16_t eigrp_sequence_encode(struct eigrp *eigrp, struct stream *s) +{ + uint16_t length = EIGRP_TLV_SEQ_BASE_LEN; + struct eigrp_interface *ei; + struct listnode *node, *node2, *nnode2; + struct eigrp_neighbor *nbr; + size_t backup_end, size_end; + int found; + + // add in the parameters TLV + backup_end = stream_get_endp(s); + stream_putw(s, EIGRP_TLV_SEQ); + size_end = s->endp; + stream_putw(s, 0x0000); + stream_putc(s, IPV4_MAX_BYTELEN); + + found = 0; + for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { + for (ALL_LIST_ELEMENTS(ei->nbrs, node2, nnode2, nbr)) { + if (nbr->multicast_queue->count > 0) { + length += (uint16_t)stream_put_ipv4( + s, nbr->src.s_addr); + found = 1; + } + } + } + + if (found == 0) { + stream_set_endp(s, backup_end); + return 0; + } + + backup_end = stream_get_endp(s); + stream_set_endp(s, size_end); + stream_putw(s, length); + stream_set_endp(s, backup_end); + + return length; +} + +/** + * @fn eigrp_sequence_encode + * + * @param[in,out] s packet stream TLV is stored to + * + * @return uint16_t number of bytes added to packet stream + * + * @par + * Part of conditional receive process + * + */ +static uint16_t eigrp_next_sequence_encode(struct eigrp *eigrp, + struct stream *s) +{ + uint16_t length = EIGRP_NEXT_SEQUENCE_TLV_SIZE; + + // add in the parameters TLV + stream_putw(s, EIGRP_TLV_NEXT_MCAST_SEQ); + stream_putw(s, EIGRP_NEXT_SEQUENCE_TLV_SIZE); + stream_putl(s, eigrp->sequence_number + 1); + + return length; +} + +/** + * @fn eigrp_hello_parameter_encode + * + * @param[in] ei pointer to interface hello packet came in on + * @param[in,out] s packet stream TLV is stored to + * + * @return uint16_t number of bytes added to packet stream + * + * @par + * Encode Parameter TLV, used to convey metric weights and the hold time. + * + * @usage + * Note the addition of K6 for the new extended metrics, and does not apply to + * older TLV packet formats. + */ +static uint16_t eigrp_hello_parameter_encode(struct eigrp_interface *ei, + struct stream *s, uint8_t flags) +{ + // add in the parameters TLV + stream_putw(s, EIGRP_TLV_PARAMETER); + stream_putw(s, EIGRP_TLV_PARAMETER_LEN); + + // if graceful shutdown is needed to be announced, send all 255 in K + // values + if (flags & EIGRP_HELLO_GRACEFUL_SHUTDOWN) { + stream_putc(s, 0xff); /* K1 */ + stream_putc(s, 0xff); /* K2 */ + stream_putc(s, 0xff); /* K3 */ + stream_putc(s, 0xff); /* K4 */ + stream_putc(s, 0xff); /* K5 */ + stream_putc(s, 0xff); /* K6 */ + } else // set k values + { + stream_putc(s, ei->eigrp->k_values[0]); /* K1 */ + stream_putc(s, ei->eigrp->k_values[1]); /* K2 */ + stream_putc(s, ei->eigrp->k_values[2]); /* K3 */ + stream_putc(s, ei->eigrp->k_values[3]); /* K4 */ + stream_putc(s, ei->eigrp->k_values[4]); /* K5 */ + stream_putc(s, ei->eigrp->k_values[5]); /* K6 */ + } + + // and set hold time value.. + stream_putw(s, ei->params.v_wait); + + return EIGRP_TLV_PARAMETER_LEN; +} + +/** + * @fn eigrp_hello_encode + * + * @param[in] ei pointer to interface hello packet came in on + * @param[in] s packet stream TLV is stored to + * @param[in] ack if non-zero, neigbors sequence packet to ack + * @param[in] flags type of hello packet + * @param[in] nbr_addr pointer to neighbor address for Peer + * Termination TLV + * + * @return eigrp_packet pointer initialize hello packet + * + * @par + * Allocate an EIGRP hello packet, and add in the the approperate TLVs + * + */ +static struct eigrp_packet *eigrp_hello_encode(struct eigrp_interface *ei, + in_addr_t addr, uint32_t ack, + uint8_t flags, + struct in_addr *nbr_addr) +{ + struct eigrp_packet *ep; + uint16_t length = EIGRP_HEADER_LEN; + + // allocate a new packet to be sent + ep = eigrp_packet_new(EIGRP_PACKET_MTU(ei->ifp->mtu), NULL); + + if (ep) { + // encode common header feilds + eigrp_packet_header_init(EIGRP_OPC_HELLO, ei->eigrp, ep->s, 0, + 0, ack); + + // encode Authentication TLV + if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) + && (ei->params.auth_keychain != NULL)) { + length += eigrp_add_authTLV_MD5_to_stream(ep->s, ei); + } else if ((ei->params.auth_type == EIGRP_AUTH_TYPE_SHA256) + && (ei->params.auth_keychain != NULL)) { + length += eigrp_add_authTLV_SHA256_to_stream(ep->s, ei); + } + + /* encode appropriate parameters to Hello packet */ + if (flags & EIGRP_HELLO_GRACEFUL_SHUTDOWN) + length += eigrp_hello_parameter_encode( + ei, ep->s, EIGRP_HELLO_GRACEFUL_SHUTDOWN); + else + length += eigrp_hello_parameter_encode( + ei, ep->s, EIGRP_HELLO_NORMAL); + + // figure out the version of code we're running + length += eigrp_sw_version_encode(ep->s); + + if (flags & EIGRP_HELLO_ADD_SEQUENCE) { + length += eigrp_sequence_encode(ei->eigrp, ep->s); + length += eigrp_next_sequence_encode(ei->eigrp, ep->s); + } + + // add in the TID list if doing multi-topology + length += eigrp_tidlist_encode(ep->s); + + /* encode Peer Termination TLV if needed */ + if (flags & EIGRP_HELLO_GRACEFUL_SHUTDOWN_NBR) + length += + eigrp_peer_termination_encode(ep->s, nbr_addr); + + // Set packet length + ep->length = length; + + // set soruce address for the hello packet + ep->dst.s_addr = addr; + + if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) + && (ei->params.auth_keychain != NULL)) { + eigrp_make_md5_digest(ei, ep->s, + EIGRP_AUTH_BASIC_HELLO_FLAG); + } else if ((ei->params.auth_type == EIGRP_AUTH_TYPE_SHA256) + && (ei->params.auth_keychain != NULL)) { + eigrp_make_sha256_digest(ei, ep->s, + EIGRP_AUTH_BASIC_HELLO_FLAG); + } + + // EIGRP Checksum + eigrp_packet_checksum(ei, ep->s, length); + } + + return (ep); +} + +/** + * @fn eigrp_hello_send + * + * @param[in] nbr neighbor the ACK should be sent to + * + * @return void + * + * @par + * Send (unicast) a hello packet with the destination address + * associated with the neighbor. The eigrp header ACK feild will be + * updated to the neighbor's sequence number to acknolodge any + * outstanding packets + */ +void eigrp_hello_send_ack(struct eigrp_neighbor *nbr) +{ + struct eigrp_packet *ep; + + /* if packet succesfully created, add it to the interface queue */ + ep = eigrp_hello_encode(nbr->ei, nbr->src.s_addr, + nbr->recv_sequence_number, EIGRP_HELLO_NORMAL, + NULL); + + if (ep) { + if (IS_DEBUG_EIGRP_PACKET(0, SEND)) + zlog_debug("Queueing [Hello] Ack Seq [%u] nbr [%pI4]", + nbr->recv_sequence_number, &nbr->src); + + /* Add packet to the top of the interface output queue*/ + eigrp_fifo_push(nbr->ei->obuf, ep); + + /* Hook thread to write packet. */ + if (nbr->ei->on_write_q == 0) { + listnode_add(nbr->ei->eigrp->oi_write_q, nbr->ei); + nbr->ei->on_write_q = 1; + } + event_add_write(master, eigrp_write, nbr->ei->eigrp, + nbr->ei->eigrp->fd, &nbr->ei->eigrp->t_write); + } +} + +/** + * @fn eigrp_hello_send + * + * @param[in] ei pointer to interface hello should be sent + * @param[in] flags type of hello packet + * @param[in] nbr_addr pointer to neighbor address for Peer + * Termination TLV + * + * @return void + * + * @par + * Build and enqueue a generic (multicast) periodic hello packet for + * sending. If no packets are currently queues, the packet will be + * sent immadiatly + */ +void eigrp_hello_send(struct eigrp_interface *ei, uint8_t flags, + struct in_addr *nbr_addr) +{ + struct eigrp_packet *ep = NULL; + + if (IS_DEBUG_EIGRP_PACKET(0, SEND)) + zlog_debug("Queueing [Hello] Interface(%s)", IF_NAME(ei)); + + /* if packet was succesfully created, then add it to the interface queue + */ + ep = eigrp_hello_encode(ei, htonl(EIGRP_MULTICAST_ADDRESS), 0, flags, + nbr_addr); + + if (ep) { + // Add packet to the top of the interface output queue + eigrp_fifo_push(ei->obuf, ep); + + /* Hook thread to write packet. */ + if (ei->on_write_q == 0) { + listnode_add(ei->eigrp->oi_write_q, ei); + ei->on_write_q = 1; + } + + if (ei->eigrp->t_write == NULL) { + if (flags & EIGRP_HELLO_GRACEFUL_SHUTDOWN) { + event_execute(master, eigrp_write, ei->eigrp, + ei->eigrp->fd, NULL); + } else { + event_add_write(master, eigrp_write, ei->eigrp, + ei->eigrp->fd, + &ei->eigrp->t_write); + } + } + } +} diff --git a/eigrpd/eigrp_interface.c b/eigrpd/eigrp_interface.c new file mode 100644 index 0000000..7bb1617 --- /dev/null +++ b/eigrpd/eigrp_interface.c @@ -0,0 +1,498 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Interface Functions. + * Copyright (C) 2013-2016 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * Frantisek Gazo + * Tomas Hvorkovy + * Martin Kontsek + * Lukas Koribsky + */ + +#include <zebra.h> + +#include "frrevent.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "memory.h" +#include "network.h" +#include "command.h" +#include "stream.h" +#include "log.h" +#include "keychain.h" +#include "vrf.h" + +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_interface.h" +#include "eigrpd/eigrp_neighbor.h" +#include "eigrpd/eigrp_packet.h" +#include "eigrpd/eigrp_zebra.h" +#include "eigrpd/eigrp_vty.h" +#include "eigrpd/eigrp_network.h" +#include "eigrpd/eigrp_topology.h" +#include "eigrpd/eigrp_fsm.h" +#include "eigrpd/eigrp_dump.h" +#include "eigrpd/eigrp_types.h" +#include "eigrpd/eigrp_metric.h" + +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_IF, "EIGRP interface"); + +struct eigrp_interface *eigrp_if_new(struct eigrp *eigrp, struct interface *ifp, + struct prefix *p) +{ + struct eigrp_interface *ei = ifp->info; + int i; + + if (ei) + return ei; + + ei = XCALLOC(MTYPE_EIGRP_IF, sizeof(struct eigrp_interface)); + + /* Set zebra interface pointer. */ + ei->ifp = ifp; + prefix_copy(&ei->address, p); + + ifp->info = ei; + listnode_add(eigrp->eiflist, ei); + + ei->type = EIGRP_IFTYPE_BROADCAST; + + /* Initialize neighbor list. */ + ei->nbrs = list_new(); + + ei->crypt_seqnum = frr_sequence32_next(); + + /* Initialize lists */ + for (i = 0; i < EIGRP_FILTER_MAX; i++) { + ei->list[i] = NULL; + ei->prefix[i] = NULL; + ei->routemap[i] = NULL; + } + + ei->eigrp = eigrp; + + ei->params.v_hello = EIGRP_HELLO_INTERVAL_DEFAULT; + ei->params.v_wait = EIGRP_HOLD_INTERVAL_DEFAULT; + ei->params.bandwidth = EIGRP_BANDWIDTH_DEFAULT; + ei->params.delay = EIGRP_DELAY_DEFAULT; + ei->params.reliability = EIGRP_RELIABILITY_DEFAULT; + ei->params.load = EIGRP_LOAD_DEFAULT; + ei->params.auth_type = EIGRP_AUTH_TYPE_NONE; + ei->params.auth_keychain = NULL; + + ei->curr_bandwidth = ifp->bandwidth; + ei->curr_mtu = ifp->mtu; + + return ei; +} + +int eigrp_if_delete_hook(struct interface *ifp) +{ + struct eigrp_interface *ei = ifp->info; + struct eigrp *eigrp; + + if (!ei) + return 0; + + list_delete(&ei->nbrs); + + eigrp = ei->eigrp; + listnode_delete(eigrp->eiflist, ei); + + eigrp_fifo_free(ei->obuf); + + XFREE(MTYPE_EIGRP_IF, ifp->info); + + return 0; +} + +static int eigrp_ifp_create(struct interface *ifp) +{ + struct eigrp_interface *ei = ifp->info; + + if (!ei) + return 0; + + ei->params.type = eigrp_default_iftype(ifp); + + eigrp_if_update(ifp); + + return 0; +} + +static int eigrp_ifp_up(struct interface *ifp) +{ + struct eigrp_interface *ei = ifp->info; + + if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) + zlog_debug("Zebra: Interface[%s] state change to up.", + ifp->name); + + if (!ei) + return 0; + + if (ei->curr_bandwidth != ifp->bandwidth) { + if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) + zlog_debug( + "Zebra: Interface[%s] bandwidth change %d -> %d.", + ifp->name, ei->curr_bandwidth, + ifp->bandwidth); + + ei->curr_bandwidth = ifp->bandwidth; + // eigrp_if_recalculate_output_cost (ifp); + } + + if (ei->curr_mtu != ifp->mtu) { + if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) + zlog_debug( + "Zebra: Interface[%s] MTU change %u -> %u.", + ifp->name, ei->curr_mtu, ifp->mtu); + + ei->curr_mtu = ifp->mtu; + /* Must reset the interface (simulate down/up) when MTU + * changes. */ + eigrp_if_reset(ifp); + return 0; + } + + eigrp_if_up(ifp->info); + + return 0; +} + +static int eigrp_ifp_down(struct interface *ifp) +{ + if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) + zlog_debug("Zebra: Interface[%s] state change to down.", + ifp->name); + + if (ifp->info) + eigrp_if_down(ifp->info); + + return 0; +} + +static int eigrp_ifp_destroy(struct interface *ifp) +{ + if (if_is_up(ifp)) + zlog_warn("Zebra: got delete of %s, but interface is still up", + ifp->name); + + if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) + zlog_debug( + "Zebra: interface delete %s index %d flags %llx metric %d mtu %d", + ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, + ifp->metric, ifp->mtu); + + if (ifp->info) + eigrp_if_free(ifp->info, INTERFACE_DOWN_BY_ZEBRA); + + return 0; +} + +struct list *eigrp_iflist; + +void eigrp_if_init(void) +{ + if_zapi_callbacks(eigrp_ifp_create, eigrp_ifp_up, + eigrp_ifp_down, eigrp_ifp_destroy); + /* Initialize Zebra interface data structure. */ + // hook_register_prio(if_add, 0, eigrp_if_new); + hook_register_prio(if_del, 0, eigrp_if_delete_hook); +} + + +void eigrp_del_if_params(struct eigrp_if_params *eip) +{ + if (eip->auth_keychain) + free(eip->auth_keychain); +} + +/* + * Set the network byte order of the 3 bytes we send + * of the mtu of the link. + */ +static void eigrp_mtu_convert(struct eigrp_metrics *metric, uint32_t host_mtu) +{ + uint32_t network_mtu = htonl(host_mtu); + uint8_t *nm = (uint8_t *)&network_mtu; + + metric->mtu[0] = nm[1]; + metric->mtu[1] = nm[2]; + metric->mtu[2] = nm[3]; +} + +int eigrp_if_up(struct eigrp_interface *ei) +{ + struct eigrp_prefix_descriptor *pe; + struct eigrp_route_descriptor *ne; + struct eigrp_metrics metric; + struct eigrp_interface *ei2; + struct listnode *node, *nnode; + struct eigrp *eigrp; + + if (ei == NULL) + return 0; + + eigrp = ei->eigrp; + eigrp_adjust_sndbuflen(eigrp, ei->ifp->mtu); + + eigrp_if_stream_set(ei); + + /* Set multicast memberships appropriately for new state. */ + eigrp_if_set_multicast(ei); + + event_add_event(master, eigrp_hello_timer, ei, (1), &ei->t_hello); + + /*Prepare metrics*/ + metric.bandwidth = eigrp_bandwidth_to_scaled(ei->params.bandwidth); + metric.delay = eigrp_delay_to_scaled(ei->params.delay); + metric.load = ei->params.load; + metric.reliability = ei->params.reliability; + eigrp_mtu_convert(&metric, ei->ifp->mtu); + metric.hop_count = 0; + metric.flags = 0; + metric.tag = 0; + + /*Add connected entry to topology table*/ + + ne = eigrp_route_descriptor_new(); + ne->ei = ei; + ne->reported_metric = metric; + ne->total_metric = metric; + ne->distance = eigrp_calculate_metrics(eigrp, metric); + ne->reported_distance = 0; + ne->adv_router = eigrp->neighbor_self; + ne->flags = EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG; + + struct prefix dest_addr; + + dest_addr = ei->address; + apply_mask(&dest_addr); + pe = eigrp_topology_table_lookup_ipv4(eigrp->topology_table, + &dest_addr); + + if (pe == NULL) { + pe = eigrp_prefix_descriptor_new(); + pe->serno = eigrp->serno; + pe->destination = (struct prefix *)prefix_ipv4_new(); + prefix_copy(pe->destination, &dest_addr); + pe->af = AF_INET; + pe->nt = EIGRP_TOPOLOGY_TYPE_CONNECTED; + + ne->prefix = pe; + pe->reported_metric = metric; + pe->state = EIGRP_FSM_STATE_PASSIVE; + pe->fdistance = eigrp_calculate_metrics(eigrp, metric); + pe->req_action |= EIGRP_FSM_NEED_UPDATE; + eigrp_prefix_descriptor_add(eigrp->topology_table, pe); + listnode_add(eigrp->topology_changes_internalIPV4, pe); + + eigrp_route_descriptor_add(eigrp, pe, ne); + + for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, ei2)) { + eigrp_update_send(ei2); + } + + pe->req_action &= ~EIGRP_FSM_NEED_UPDATE; + listnode_delete(eigrp->topology_changes_internalIPV4, pe); + } else { + struct eigrp_fsm_action_message msg; + + ne->prefix = pe; + eigrp_route_descriptor_add(eigrp, pe, ne); + + msg.packet_type = EIGRP_OPC_UPDATE; + msg.eigrp = eigrp; + msg.data_type = EIGRP_CONNECTED; + msg.adv_router = NULL; + msg.entry = ne; + msg.prefix = pe; + + eigrp_fsm_event(&msg); + } + + return 1; +} + +int eigrp_if_down(struct eigrp_interface *ei) +{ + struct listnode *node, *nnode; + struct eigrp_neighbor *nbr; + + if (ei == NULL) + return 0; + + /* Shutdown packet reception and sending */ + EVENT_OFF(ei->t_hello); + + eigrp_if_stream_unset(ei); + + /*Set infinite metrics to routes learned by this interface and start + * query process*/ + for (ALL_LIST_ELEMENTS(ei->nbrs, node, nnode, nbr)) { + eigrp_nbr_delete(nbr); + } + + return 1; +} + +void eigrp_if_stream_set(struct eigrp_interface *ei) +{ + /* set output fifo queue. */ + if (ei->obuf == NULL) + ei->obuf = eigrp_fifo_new(); +} + +void eigrp_if_stream_unset(struct eigrp_interface *ei) +{ + struct eigrp *eigrp = ei->eigrp; + + if (ei->on_write_q) { + listnode_delete(eigrp->oi_write_q, ei); + if (list_isempty(eigrp->oi_write_q)) + event_cancel(&(eigrp->t_write)); + ei->on_write_q = 0; + } +} + +bool eigrp_if_is_passive(struct eigrp_interface *ei) +{ + if (ei->params.passive_interface == EIGRP_IF_ACTIVE) + return false; + + if (ei->eigrp->passive_interface_default == EIGRP_IF_ACTIVE) + return false; + + return true; +} + +void eigrp_if_set_multicast(struct eigrp_interface *ei) +{ + if (!eigrp_if_is_passive(ei)) { + /* The interface should belong to the EIGRP-all-routers group. + */ + if (!ei->member_allrouters + && (eigrp_if_add_allspfrouters(ei->eigrp, &ei->address, + ei->ifp->ifindex) + >= 0)) + /* Set the flag only if the system call to join + * succeeded. */ + ei->member_allrouters = true; + } else { + /* The interface should NOT belong to the EIGRP-all-routers + * group. */ + if (ei->member_allrouters) { + /* Only actually drop if this is the last reference */ + eigrp_if_drop_allspfrouters(ei->eigrp, &ei->address, + ei->ifp->ifindex); + /* Unset the flag regardless of whether the system call + to leave + the group succeeded, since it's much safer to assume + that + we are not a member. */ + ei->member_allrouters = false; + } + } +} + +uint8_t eigrp_default_iftype(struct interface *ifp) +{ + if (if_is_pointopoint(ifp)) + return EIGRP_IFTYPE_POINTOPOINT; + else if (if_is_loopback(ifp)) + return EIGRP_IFTYPE_LOOPBACK; + else + return EIGRP_IFTYPE_BROADCAST; +} + +void eigrp_if_free(struct eigrp_interface *ei, int source) +{ + struct prefix dest_addr; + struct eigrp_prefix_descriptor *pe; + struct eigrp *eigrp = ei->eigrp; + + if (source == INTERFACE_DOWN_BY_VTY) { + event_cancel(&ei->t_hello); + eigrp_hello_send(ei, EIGRP_HELLO_GRACEFUL_SHUTDOWN, NULL); + } + + dest_addr = ei->address; + apply_mask(&dest_addr); + pe = eigrp_topology_table_lookup_ipv4(eigrp->topology_table, + &dest_addr); + if (pe) + eigrp_prefix_descriptor_delete(eigrp, eigrp->topology_table, + pe); + + eigrp_if_down(ei); + + listnode_delete(ei->eigrp->eiflist, ei); +} + +/* Simulate down/up on the interface. This is needed, for example, when + the MTU changes. */ +void eigrp_if_reset(struct interface *ifp) +{ + struct eigrp_interface *ei = ifp->info; + + if (!ei) + return; + + eigrp_if_down(ei); + eigrp_if_up(ei); +} + +struct eigrp_interface *eigrp_if_lookup_by_local_addr(struct eigrp *eigrp, + struct interface *ifp, + struct in_addr address) +{ + struct listnode *node; + struct eigrp_interface *ei; + + for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { + if (ifp && ei->ifp != ifp) + continue; + + if (IPV4_ADDR_SAME(&address, &ei->address.u.prefix4)) + return ei; + } + + return NULL; +} + +/** + * @fn eigrp_if_lookup_by_name + * + * @param[in] eigrp EIGRP process + * @param[in] if_name Name of the interface + * + * @return struct eigrp_interface * + * + * @par + * Function is used for lookup interface by name. + */ +struct eigrp_interface *eigrp_if_lookup_by_name(struct eigrp *eigrp, + const char *if_name) +{ + struct eigrp_interface *ei; + struct listnode *node; + + /* iterate over all eigrp interfaces */ + for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { + /* compare int name with eigrp interface's name */ + if (strcmp(ei->ifp->name, if_name) == 0) { + return ei; + } + } + + return NULL; +} diff --git a/eigrpd/eigrp_interface.h b/eigrpd/eigrp_interface.h new file mode 100644 index 0000000..76f9c1b --- /dev/null +++ b/eigrpd/eigrp_interface.h @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Interface Functions. + * Copyright (C) 2013-2016 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * Frantisek Gazo + * Tomas Hvorkovy + * Martin Kontsek + * Lukas Koribsky + */ + +#ifndef _ZEBRA_EIGRP_INTERFACE_H_ +#define _ZEBRA_EIGRP_INTERFACE_H_ + +/*Prototypes*/ +extern void eigrp_if_init(void); +extern int eigrp_if_new_hook(struct interface *); +extern int eigrp_if_delete_hook(struct interface *); + +extern bool eigrp_if_is_passive(struct eigrp_interface *ei); +extern void eigrp_del_if_params(struct eigrp_if_params *); +extern struct eigrp_interface *eigrp_if_new(struct eigrp *, struct interface *, + struct prefix *); +extern int eigrp_if_up(struct eigrp_interface *); +extern void eigrp_if_stream_set(struct eigrp_interface *); +extern void eigrp_if_set_multicast(struct eigrp_interface *); +extern uint8_t eigrp_default_iftype(struct interface *); +extern void eigrp_if_free(struct eigrp_interface *, int); +extern int eigrp_if_down(struct eigrp_interface *); +extern void eigrp_if_stream_unset(struct eigrp_interface *); + +extern struct eigrp_interface *eigrp_if_lookup_by_local_addr(struct eigrp *, + struct interface *, + struct in_addr); +extern struct eigrp_interface *eigrp_if_lookup_by_name(struct eigrp *, + const char *); + +/* Simulate down/up on the interface. */ +extern void eigrp_if_reset(struct interface *); + +#endif /* ZEBRA_EIGRP_INTERFACE_H_ */ diff --git a/eigrpd/eigrp_macros.h b/eigrpd/eigrp_macros.h new file mode 100644 index 0000000..75f1d7e --- /dev/null +++ b/eigrpd/eigrp_macros.h @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Macros Definition. + * Copyright (C) 2013-2014 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + */ + +#ifndef _ZEBRA_EIGRP_MACROS_H_ +#define _ZEBRA_EIGRP_MACROS_H_ + +//-------------------------------------------------------------------------- + +#define EIGRP_IF_STRING_MAXLEN 40 +#define IF_NAME(I) eigrp_if_name_string ((I)) + +//-------------------------------------------------------------------------- + +#define EIGRP_PACKET_MTU(mtu) ((mtu) - (sizeof(struct ip))) + +/* Topology Macros */ + + +/* FSM macros*/ +#define EIGRP_FSM_EVENT_SCHEDULE(I, E) \ + event_add_event(master, eigrp_fsm_event, (I), (E)) + +#endif /* _ZEBRA_EIGRP_MACROS_H_ */ diff --git a/eigrpd/eigrp_main.c b/eigrpd/eigrp_main.c new file mode 100644 index 0000000..6d7443b --- /dev/null +++ b/eigrpd/eigrp_main.c @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Main Routine. + * Copyright (C) 2013-2015 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * Frantisek Gazo + * Tomas Hvorkovy + * Martin Kontsek + * Lukas Koribsky + */ +#include <zebra.h> + +#include <lib/version.h> +#include "getopt.h" +#include "frrevent.h" +#include "prefix.h" +#include "linklist.h" +#include "if.h" +#include "vector.h" +#include "vty.h" +#include "command.h" +#include "filter.h" +#include "plist.h" +#include "stream.h" +#include "log.h" +#include "memory.h" +#include "privs.h" +#include "sigevent.h" +#include "zclient.h" +#include "keychain.h" +#include "distribute.h" +#include "libfrr.h" +#include "routemap.h" +//#include "if_rmap.h" + +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_dump.h" +#include "eigrpd/eigrp_interface.h" +#include "eigrpd/eigrp_neighbor.h" +#include "eigrpd/eigrp_packet.h" +#include "eigrpd/eigrp_vty.h" +#include "eigrpd/eigrp_zebra.h" +#include "eigrpd/eigrp_network.h" +#include "eigrpd/eigrp_snmp.h" +#include "eigrpd/eigrp_filter.h" +#include "eigrpd/eigrp_errors.h" +#include "eigrpd/eigrp_vrf.h" +#include "eigrpd/eigrp_cli.h" +#include "eigrpd/eigrp_yang.h" +//#include "eigrpd/eigrp_routemap.h" + +/* eigprd privileges */ +zebra_capabilities_t _caps_p[] = { + ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, +}; + +struct zebra_privs_t eigrpd_privs = { +#if defined(FRR_USER) && defined(FRR_GROUP) + .user = FRR_USER, + .group = FRR_GROUP, +#endif +#if defined(VTY_GROUP) + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = array_size(_caps_p), + .cap_num_i = 0}; + +/* EIGRPd options. */ +struct option longopts[] = {{0}}; + +/* Master of threads. */ +struct event_loop *master; + +/* Forward declaration of daemon info structure. */ +static struct frr_daemon_info eigrpd_di; + +/* SIGHUP handler. */ +static void sighup(void) +{ + zlog_info("SIGHUP received"); + + /* Reload config file. */ + vty_read_config(NULL, eigrpd_di.config_file, config_default); +} + +/* SIGINT / SIGTERM handler. */ +static void sigint(void) +{ + zlog_notice("Terminating on signal"); + eigrp_terminate(); + + exit(0); +} + +/* SIGUSR1 handler. */ +static void sigusr1(void) +{ + zlog_rotate(); +} + +struct frr_signal_t eigrp_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 eigrpd_yang_modules[] = { + &frr_eigrpd_info, + &frr_filter_info, + &frr_interface_info, + &frr_route_map_info, + &frr_vrf_info, +}; + +FRR_DAEMON_INFO(eigrpd, EIGRP, .vty_port = EIGRP_VTY_PORT, + + .proghelp = "Implementation of the EIGRP routing protocol.", + + .signals = eigrp_signals, + .n_signals = array_size(eigrp_signals), + + .privs = &eigrpd_privs, .yang_modules = eigrpd_yang_modules, + .n_yang_modules = array_size(eigrpd_yang_modules), +); + +/* EIGRPd main routine. */ +int main(int argc, char **argv, char **envp) +{ + frr_preinit(&eigrpd_di, argc, argv); + frr_opt_add("", longopts, ""); + + while (1) { + int opt; + + opt = frr_getopt(argc, argv, NULL); + + if (opt == EOF) + break; + + switch (opt) { + case 0: + break; + default: + frr_help_exit(1); + } + } + + eigrp_sw_version_initialize(); + + /* EIGRP master init. */ + eigrp_master_init(); + eigrp_om->master = frr_init(); + master = eigrp_om->master; + + eigrp_error_init(); + eigrp_vrf_init(); + vrf_init(NULL, NULL, NULL, NULL); + + /*EIGRPd init*/ + eigrp_if_init(); + eigrp_zebra_init(); + eigrp_debug_init(); + + /* Get configuration file. */ + /* EIGRP VTY inits */ + eigrp_vty_init(); + keychain_init(); + eigrp_vty_show_init(); + eigrp_cli_init(); + +#ifdef HAVE_SNMP + eigrp_snmp_init(); +#endif /* HAVE_SNMP */ + + /* Access list install. */ + access_list_init(); + access_list_add_hook(eigrp_distribute_update_all_wrapper); + access_list_delete_hook(eigrp_distribute_update_all_wrapper); + + /* Prefix list initialize.*/ + prefix_list_init(); + prefix_list_add_hook(eigrp_distribute_update_all); + prefix_list_delete_hook(eigrp_distribute_update_all); + + /* + * XXX: This is just to get the CLI installed to suppress VTYSH errors. + * Routemaps in EIGRP are not yet functional. + */ + route_map_init(); + /*eigrp_route_map_init(); + route_map_add_hook (eigrp_rmap_update); + route_map_delete_hook (eigrp_rmap_update);*/ + /*if_rmap_init (EIGRP_NODE); */ + + frr_config_fork(); + frr_run(master); + + /* Not reached. */ + return 0; +} diff --git a/eigrpd/eigrp_metric.c b/eigrpd/eigrp_metric.c new file mode 100644 index 0000000..1662f48 --- /dev/null +++ b/eigrpd/eigrp_metric.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Metric Math Functions. + * Copyright (C) 2013-2016 + * Authors: + * Donnie Savage + */ + +#include <zebra.h> + +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_types.h" +#include "eigrpd/eigrp_metric.h" + +eigrp_scaled_t eigrp_bandwidth_to_scaled(eigrp_bandwidth_t bandwidth) +{ + eigrp_bandwidth_t scaled = EIGRP_BANDWIDTH_MAX; + + if (bandwidth != EIGRP_BANDWIDTH_MAX) { + scaled = (EIGRP_CLASSIC_SCALER * EIGRP_BANDWIDTH_SCALER); + scaled = scaled / bandwidth; + + scaled = scaled ? scaled : EIGRP_BANDWIDTH_MIN; + } + + scaled = (scaled < EIGRP_METRIC_MAX) ? scaled : EIGRP_METRIC_MAX; + return (eigrp_scaled_t)scaled; +} + +eigrp_bandwidth_t eigrp_scaled_to_bandwidth(eigrp_scaled_t scaled) +{ + eigrp_bandwidth_t bandwidth = EIGRP_BANDWIDTH_MAX; + + if (scaled != EIGRP_CLASSIC_MAX) { + bandwidth = (EIGRP_CLASSIC_SCALER * EIGRP_BANDWIDTH_SCALER); + bandwidth = scaled * bandwidth; + bandwidth = (bandwidth < EIGRP_METRIC_MAX) + ? bandwidth + : EIGRP_BANDWIDTH_MAX; + } + + return bandwidth; +} + +eigrp_scaled_t eigrp_delay_to_scaled(eigrp_delay_t delay) +{ + delay = delay ? delay : EIGRP_DELAY_MIN; + return delay * EIGRP_CLASSIC_SCALER; +} + +eigrp_delay_t eigrp_scaled_to_delay(eigrp_scaled_t scaled) +{ + scaled = scaled / EIGRP_CLASSIC_SCALER; + scaled = scaled ? scaled : EIGRP_DELAY_MIN; + + return scaled; +} + +eigrp_metric_t eigrp_calculate_metrics(struct eigrp *eigrp, + struct eigrp_metrics metric) +{ + eigrp_metric_t composite = 0; + + if (metric.delay == EIGRP_MAX_METRIC) + return EIGRP_METRIC_MAX; + + /* + * EIGRP Composite = + * {K1*BW+[(K2*BW)/(256-load)]+(K3*delay)}*{K5/(reliability+K4)} + */ + + if (eigrp->k_values[0]) + composite += ((eigrp_metric_t)eigrp->k_values[0] * + (eigrp_metric_t)metric.bandwidth); + if (eigrp->k_values[1]) + composite += (((eigrp_metric_t)eigrp->k_values[1] * + (eigrp_metric_t)metric.bandwidth) / + (256 - metric.load)); + if (eigrp->k_values[2]) + composite += ((eigrp_metric_t)eigrp->k_values[2] * + (eigrp_metric_t)metric.delay); + if (eigrp->k_values[3] && !eigrp->k_values[4]) + composite *= (eigrp_metric_t)eigrp->k_values[3]; + if (!eigrp->k_values[3] && eigrp->k_values[4]) + composite *= ((eigrp_metric_t)eigrp->k_values[4] / + (eigrp_metric_t)metric.reliability); + if (eigrp->k_values[3] && eigrp->k_values[4]) + composite *= (((eigrp_metric_t)eigrp->k_values[4] / + (eigrp_metric_t)metric.reliability) + + (eigrp_metric_t)eigrp->k_values[3]); + + composite = + (composite <= EIGRP_METRIC_MAX) ? composite : EIGRP_METRIC_MAX; + + return composite; +} + +eigrp_metric_t +eigrp_calculate_total_metrics(struct eigrp *eigrp, + struct eigrp_route_descriptor *entry) +{ + struct eigrp_interface *ei = entry->ei; + eigrp_delay_t temp_delay; + eigrp_bandwidth_t bw; + + entry->total_metric = entry->reported_metric; + temp_delay = entry->total_metric.delay + + eigrp_delay_to_scaled(ei->params.delay); + + entry->total_metric.delay = temp_delay > EIGRP_METRIC_MAX_CLASSIC + ? EIGRP_METRIC_MAX_CLASSIC + : temp_delay; + + bw = eigrp_bandwidth_to_scaled(ei->params.bandwidth); + entry->total_metric.bandwidth = entry->total_metric.bandwidth > bw + ? bw + : entry->total_metric.bandwidth; + + return eigrp_calculate_metrics(eigrp, entry->total_metric); +} + +bool eigrp_metrics_is_same(struct eigrp_metrics metric1, + struct eigrp_metrics metric2) +{ + if ((metric1.bandwidth == metric2.bandwidth) + && (metric1.delay == metric2.delay) + && (metric1.hop_count == metric2.hop_count) + && (metric1.load == metric2.load) + && (metric1.reliability == metric2.reliability) + && (metric1.mtu[0] == metric2.mtu[0]) + && (metric1.mtu[1] == metric2.mtu[1]) + && (metric1.mtu[2] == metric2.mtu[2])) { + return true; + } + + return false; /* if different */ +} diff --git a/eigrpd/eigrp_metric.h b/eigrpd/eigrp_metric.h new file mode 100644 index 0000000..d4a8d09 --- /dev/null +++ b/eigrpd/eigrp_metric.h @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Metric Math Functions. + * Copyright (C) 2013-2016 + * Authors: + * Donnie Savage + */ + +#ifndef _ZEBRA_EIGRP_METRIC_H_ +#define _ZEBRA_EIGRP_METRIC_H_ + +/* Constants */ +#define EIGRP_BANDWIDTH_MIN 0x1ull /* 1 */ +#define EIGRP_BANDWIDTH_SCALER 10000000ull /* Inversion value */ +#define EIGRP_BANDWIDTH_MAX 0xffffffffffffffffull /* 1.84467441x10^19 */ + +#define EIGRP_DELAY_MIN 0x1ull /* 1 */ +#define EIGRP_DELAY_PICO 1000000ull +#define EIGRP_DELAY_MAX 0xffffffffffffffffull /* 1.84467441x10^19 */ + +#define EIGRP_MAX_LOAD 256 +#define EIGRP_MAX_HOPS 100 + +#define EIGRP_INACCESSIBLE 0xFFFFFFFFFFFFFFFFull + +#define EIGRP_METRIC_MAX 0xffffffffffffffffull /* 1.84467441x10^19 */ +#define EIGRP_METRIC_MAX_CLASSIC 0xffffffff +#define EIGRP_METRIC_SCALER 65536 /* CLASSIC to WIDE conversion */ + +#define EIGRP_CLASSIC_MAX 0xffffffff /* 4294967295 */ +#define EIGRP_CLASSIC_SCALER 256 /* IGRP to EIGRP conversion */ + + +/* Prototypes */ +extern eigrp_scaled_t eigrp_bandwidth_to_scaled(eigrp_bandwidth_t bw); +extern eigrp_bandwidth_t eigrp_scaled_to_bandwidth(eigrp_scaled_t scale); +extern eigrp_scaled_t eigrp_delay_to_scaled(eigrp_delay_t delay); +extern eigrp_delay_t eigrp_scaled_to_delay(eigrp_scaled_t scale); + +extern eigrp_metric_t eigrp_calculate_metrics(struct eigrp *eigrp, + struct eigrp_metrics metric); +extern eigrp_metric_t +eigrp_calculate_total_metrics(struct eigrp *eigrp, + struct eigrp_route_descriptor *rd); +extern bool eigrp_metrics_is_same(struct eigrp_metrics m1, + struct eigrp_metrics m2); + +#endif /* _ZEBRA_EIGRP_METRIC_H_ */ diff --git a/eigrpd/eigrp_neighbor.c b/eigrpd/eigrp_neighbor.c new file mode 100644 index 0000000..25209c3 --- /dev/null +++ b/eigrpd/eigrp_neighbor.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Neighbor Handling. + * Copyright (C) 2013-2016 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * Frantisek Gazo + * Tomas Hvorkovy + * Martin Kontsek + * Lukas Koribsky + */ + +#include <zebra.h> + +#include "linklist.h" +#include "prefix.h" +#include "memory.h" +#include "command.h" +#include "frrevent.h" +#include "stream.h" +#include "table.h" +#include "log.h" +#include "keychain.h" +#include "vty.h" + +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_interface.h" +#include "eigrpd/eigrp_neighbor.h" +#include "eigrpd/eigrp_dump.h" +#include "eigrpd/eigrp_packet.h" +#include "eigrpd/eigrp_zebra.h" +#include "eigrpd/eigrp_vty.h" +#include "eigrpd/eigrp_network.h" +#include "eigrpd/eigrp_topology.h" +#include "eigrpd/eigrp_errors.h" + +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_NEIGHBOR, "EIGRP neighbor"); + +struct eigrp_neighbor *eigrp_nbr_new(struct eigrp_interface *ei) +{ + struct eigrp_neighbor *nbr; + + /* Allcate new neighbor. */ + nbr = XCALLOC(MTYPE_EIGRP_NEIGHBOR, sizeof(struct eigrp_neighbor)); + + /* Relate neighbor to the interface. */ + nbr->ei = ei; + + /* Set default values. */ + eigrp_nbr_state_set(nbr, EIGRP_NEIGHBOR_DOWN); + + return nbr; +} + +/** + *@fn void dissect_eigrp_sw_version (tvbuff_t *tvb, proto_tree *tree, + * proto_item *ti) + * + * @par + * Create a new neighbor structure and initalize it. + */ +static struct eigrp_neighbor *eigrp_nbr_add(struct eigrp_interface *ei, + struct eigrp_header *eigrph, + struct ip *iph) +{ + struct eigrp_neighbor *nbr; + + nbr = eigrp_nbr_new(ei); + nbr->src = iph->ip_src; + + return nbr; +} + +struct eigrp_neighbor *eigrp_nbr_get(struct eigrp_interface *ei, + struct eigrp_header *eigrph, + struct ip *iph) +{ + struct eigrp_neighbor *nbr; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(ei->nbrs, node, nnode, nbr)) { + if (iph->ip_src.s_addr == nbr->src.s_addr) { + return nbr; + } + } + + nbr = eigrp_nbr_add(ei, eigrph, iph); + listnode_add(ei->nbrs, nbr); + + return nbr; +} + +/** + * @fn eigrp_nbr_lookup_by_addr + * + * @param[in] ei EIGRP interface + * @param[in] nbr_addr Address of neighbor + * + * @return void + * + * @par + * Function is used for neighbor lookup by address + * in specified interface. + */ +struct eigrp_neighbor *eigrp_nbr_lookup_by_addr(struct eigrp_interface *ei, + struct in_addr *addr) +{ + struct eigrp_neighbor *nbr; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(ei->nbrs, node, nnode, nbr)) { + if (addr->s_addr == nbr->src.s_addr) { + return nbr; + } + } + + return NULL; +} + +/** + * @fn eigrp_nbr_lookup_by_addr_process + * + * @param[in] eigrp EIGRP process + * @param[in] nbr_addr Address of neighbor + * + * @return void + * + * @par + * Function is used for neighbor lookup by address + * in whole EIGRP process. + */ +struct eigrp_neighbor *eigrp_nbr_lookup_by_addr_process(struct eigrp *eigrp, + struct in_addr nbr_addr) +{ + struct eigrp_interface *ei; + struct listnode *node, *node2, *nnode2; + struct eigrp_neighbor *nbr; + + /* iterate over all eigrp interfaces */ + for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { + /* iterate over all neighbors on eigrp interface */ + for (ALL_LIST_ELEMENTS(ei->nbrs, node2, nnode2, nbr)) { + /* compare if neighbor address is same as arg address */ + if (nbr->src.s_addr == nbr_addr.s_addr) { + return nbr; + } + } + } + + return NULL; +} + + +/* Delete specified EIGRP neighbor from interface. */ +void eigrp_nbr_delete(struct eigrp_neighbor *nbr) +{ + eigrp_nbr_state_set(nbr, EIGRP_NEIGHBOR_DOWN); + if (nbr->ei) + eigrp_topology_neighbor_down(nbr->ei->eigrp, nbr); + + /* Cancel all events. */ /* Thread lookup cost would be negligible. */ + event_cancel_event(master, nbr); + eigrp_fifo_free(nbr->multicast_queue); + eigrp_fifo_free(nbr->retrans_queue); + EVENT_OFF(nbr->t_holddown); + + if (nbr->ei) + listnode_delete(nbr->ei->nbrs, nbr); + XFREE(MTYPE_EIGRP_NEIGHBOR, nbr); +} + +void holddown_timer_expired(struct event *thread) +{ + struct eigrp_neighbor *nbr = EVENT_ARG(thread); + struct eigrp *eigrp = nbr->ei->eigrp; + + zlog_info("Neighbor %pI4 (%s) is down: holding time expired", &nbr->src, + ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id)); + nbr->state = EIGRP_NEIGHBOR_DOWN; + eigrp_nbr_delete(nbr); +} + +uint8_t eigrp_nbr_state_get(struct eigrp_neighbor *nbr) +{ + return (nbr->state); +} + +void eigrp_nbr_state_set(struct eigrp_neighbor *nbr, uint8_t state) +{ + nbr->state = state; + + if (eigrp_nbr_state_get(nbr) == EIGRP_NEIGHBOR_DOWN) { + // reset all the seq/ack counters + nbr->recv_sequence_number = 0; + nbr->init_sequence_number = 0; + nbr->retrans_counter = 0; + + // Kvalues + nbr->K1 = EIGRP_K1_DEFAULT; + nbr->K2 = EIGRP_K2_DEFAULT; + nbr->K3 = EIGRP_K3_DEFAULT; + nbr->K4 = EIGRP_K4_DEFAULT; + nbr->K5 = EIGRP_K5_DEFAULT; + nbr->K6 = EIGRP_K6_DEFAULT; + + // hold time.. + nbr->v_holddown = EIGRP_HOLD_INTERVAL_DEFAULT; + EVENT_OFF(nbr->t_holddown); + + /* out with the old */ + if (nbr->multicast_queue) + eigrp_fifo_free(nbr->multicast_queue); + if (nbr->retrans_queue) + eigrp_fifo_free(nbr->retrans_queue); + + /* in with the new */ + nbr->retrans_queue = eigrp_fifo_new(); + nbr->multicast_queue = eigrp_fifo_new(); + + nbr->crypt_seqnum = 0; + } +} + +const char *eigrp_nbr_state_str(struct eigrp_neighbor *nbr) +{ + const char *state; + switch (nbr->state) { + case EIGRP_NEIGHBOR_DOWN: + state = "Down"; + break; + case EIGRP_NEIGHBOR_PENDING: + state = "Waiting for Init"; + break; + case EIGRP_NEIGHBOR_UP: + state = "Up"; + break; + default: + state = "Unknown"; + break; + } + + return (state); +} + +void eigrp_nbr_state_update(struct eigrp_neighbor *nbr) +{ + switch (nbr->state) { + case EIGRP_NEIGHBOR_DOWN: { + /*Start Hold Down Timer for neighbor*/ + // EVENT_OFF(nbr->t_holddown); + // EVENT_TIMER_ON(master, nbr->t_holddown, + // holddown_timer_expired, + // nbr, nbr->v_holddown); + break; + } + case EIGRP_NEIGHBOR_PENDING: { + /*Reset Hold Down Timer for neighbor*/ + EVENT_OFF(nbr->t_holddown); + event_add_timer(master, holddown_timer_expired, nbr, + nbr->v_holddown, &nbr->t_holddown); + break; + } + case EIGRP_NEIGHBOR_UP: { + /*Reset Hold Down Timer for neighbor*/ + EVENT_OFF(nbr->t_holddown); + event_add_timer(master, holddown_timer_expired, nbr, + nbr->v_holddown, &nbr->t_holddown); + break; + } + } +} + +int eigrp_nbr_count_get(struct eigrp *eigrp) +{ + struct eigrp_interface *iface; + struct listnode *node, *node2, *nnode2; + struct eigrp_neighbor *nbr; + uint32_t counter; + + counter = 0; + for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, iface)) { + for (ALL_LIST_ELEMENTS(iface->nbrs, node2, nnode2, nbr)) { + if (nbr->state == EIGRP_NEIGHBOR_UP) { + counter++; + } + } + } + return counter; +} + +/** + * @fn eigrp_nbr_hard_restart + * + * @param[in] nbr Neighbor who would receive hard restart + * @param[in] vty Virtual terminal for log output + * @return void + * + * @par + * Function used for executing hard restart for neighbor: + * Send Hello packet with Peer Termination TLV with + * neighbor's address, set it's state to DOWN and delete the neighbor + */ +void eigrp_nbr_hard_restart(struct eigrp_neighbor *nbr, struct vty *vty) +{ + struct eigrp *eigrp = nbr->ei->eigrp; + + zlog_debug("Neighbor %pI4 (%s) is down: manually cleared", &nbr->src, + ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id)); + if (vty != NULL) { + vty_time_print(vty, 0); + vty_out(vty, "Neighbor %pI4 (%s) is down: manually cleared\n", + &nbr->src, + ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id)); + } + + /* send Hello with Peer Termination TLV */ + eigrp_hello_send(nbr->ei, EIGRP_HELLO_GRACEFUL_SHUTDOWN_NBR, + &(nbr->src)); + /* set neighbor to DOWN */ + nbr->state = EIGRP_NEIGHBOR_DOWN; + /* delete neighbor */ + eigrp_nbr_delete(nbr); +} + +int eigrp_nbr_split_horizon_check(struct eigrp_route_descriptor *ne, + struct eigrp_interface *ei) +{ + if (ne->distance == EIGRP_MAX_METRIC) + return 0; + + return (ne->ei == ei); +} diff --git a/eigrpd/eigrp_neighbor.h b/eigrpd/eigrp_neighbor.h new file mode 100644 index 0000000..2ccc89c --- /dev/null +++ b/eigrpd/eigrp_neighbor.h @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Neighbor Handling. + * Copyright (C) 2013-2016 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * Frantisek Gazo + * Tomas Hvorkovy + * Martin Kontsek + * Lukas Koribsky + */ + +#ifndef _ZEBRA_EIGRP_NEIGHBOR_H +#define _ZEBRA_EIGRP_NEIGHBOR_H + +/* Prototypes */ +extern struct eigrp_neighbor *eigrp_nbr_get(struct eigrp_interface *ei, + struct eigrp_header *, + struct ip *addr); +extern struct eigrp_neighbor *eigrp_nbr_new(struct eigrp_interface *ei); +extern void eigrp_nbr_delete(struct eigrp_neighbor *neigh); + +extern void holddown_timer_expired(struct event *thread); + +extern int eigrp_neighborship_check(struct eigrp_neighbor *neigh, + struct TLV_Parameter_Type *tlv); +extern void eigrp_nbr_state_update(struct eigrp_neighbor *neigh); +extern void eigrp_nbr_state_set(struct eigrp_neighbor *neigh, uint8_t state); +extern uint8_t eigrp_nbr_state_get(struct eigrp_neighbor *neigh); +extern int eigrp_nbr_count_get(struct eigrp *eigrp); +extern const char *eigrp_nbr_state_str(struct eigrp_neighbor *neigh); +extern struct eigrp_neighbor * +eigrp_nbr_lookup_by_addr(struct eigrp_interface *ei, struct in_addr *addr); +extern struct eigrp_neighbor * +eigrp_nbr_lookup_by_addr_process(struct eigrp *eigrp, struct in_addr addr); +extern void eigrp_nbr_hard_restart(struct eigrp_neighbor *nbr, struct vty *vty); + +extern int eigrp_nbr_split_horizon_check(struct eigrp_route_descriptor *ne, + struct eigrp_interface *ei); +#endif /* _ZEBRA_EIGRP_NEIGHBOR_H */ diff --git a/eigrpd/eigrp_network.c b/eigrpd/eigrp_network.c new file mode 100644 index 0000000..ef567fe --- /dev/null +++ b/eigrpd/eigrp_network.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Network Related Functions. + * Copyright (C) 2013-2014 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + */ + +#include <zebra.h> + +#include "frrevent.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "sockunion.h" +#include "log.h" +#include "sockopt.h" +#include "privs.h" +#include "table.h" +#include "vty.h" +#include "lib_errors.h" + +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_interface.h" +#include "eigrpd/eigrp_neighbor.h" +#include "eigrpd/eigrp_packet.h" +#include "eigrpd/eigrp_zebra.h" +#include "eigrpd/eigrp_vty.h" +#include "eigrpd/eigrp_network.h" + +static int eigrp_network_match_iface(const struct prefix *connected_prefix, + const struct prefix *prefix); +static void eigrp_network_run_interface(struct eigrp *, struct prefix *, + struct interface *); + +int eigrp_sock_init(struct vrf *vrf) +{ + int eigrp_sock = -1; + int ret; +#ifdef IP_HDRINCL + int hincl = 1; +#endif + + if (!vrf) + return eigrp_sock; + + frr_with_privs(&eigrpd_privs) { + eigrp_sock = vrf_socket( + AF_INET, SOCK_RAW, IPPROTO_EIGRPIGP, vrf->vrf_id, + vrf->vrf_id != VRF_DEFAULT ? vrf->name : NULL); + if (eigrp_sock < 0) { + zlog_err("%s: socket: %s", + __func__, safe_strerror(errno)); + exit(1); + } + +#ifdef IP_HDRINCL + /* we will include IP header with packet */ + ret = setsockopt(eigrp_sock, IPPROTO_IP, IP_HDRINCL, &hincl, + sizeof(hincl)); + if (ret < 0) { + zlog_warn("Can't set IP_HDRINCL option for fd %d: %s", + eigrp_sock, safe_strerror(errno)); + } +#elif defined(IPTOS_PREC_INTERNETCONTROL) +#warning "IP_HDRINCL not available on this system" +#warning "using IPTOS_PREC_INTERNETCONTROL" + ret = setsockopt_ipv4_tos(eigrp_sock, + IPTOS_PREC_INTERNETCONTROL); + if (ret < 0) { + zlog_warn("can't set sockopt IP_TOS %d to socket %d: %s", + tos, eigrp_sock, safe_strerror(errno)); + close(eigrp_sock); /* Prevent sd leak. */ + return ret; + } +#else /* !IPTOS_PREC_INTERNETCONTROL */ +#warning "IP_HDRINCL not available, nor is IPTOS_PREC_INTERNETCONTROL" + zlog_warn("IP_HDRINCL option not available"); +#endif /* IP_HDRINCL */ + + ret = setsockopt_ifindex(AF_INET, eigrp_sock, 1); + if (ret < 0) + zlog_warn("Can't set pktinfo option for fd %d", + eigrp_sock); + } + + return eigrp_sock; +} + +void eigrp_adjust_sndbuflen(struct eigrp *eigrp, unsigned int buflen) +{ + int newbuflen; + /* Check if any work has to be done at all. */ + if (eigrp->maxsndbuflen >= buflen) + return; + + /* Now we try to set SO_SNDBUF to what our caller has requested + * (the MTU of a newly added interface). However, if the OS has + * truncated the actual buffer size to somewhat less size, try + * to detect it and update our records appropriately. The OS + * may allocate more buffer space, than requested, this isn't + * a error. + */ + setsockopt_so_sendbuf(eigrp->fd, buflen); + newbuflen = getsockopt_so_sendbuf(eigrp->fd); + if (newbuflen < 0 || newbuflen < (int)buflen) + zlog_warn("%s: tried to set SO_SNDBUF to %u, but got %d", + __func__, buflen, newbuflen); + if (newbuflen >= 0) + eigrp->maxsndbuflen = (unsigned int)newbuflen; + else + zlog_warn("%s: failed to get SO_SNDBUF", __func__); +} + +int eigrp_if_ipmulticast(struct eigrp *top, struct prefix *p, + unsigned int ifindex) +{ + uint8_t val; + int ret, len; + + val = 0; + len = sizeof(val); + + /* Prevent receiving self-origined multicast packets. */ + ret = setsockopt(top->fd, IPPROTO_IP, IP_MULTICAST_LOOP, (void *)&val, + len); + if (ret < 0) + zlog_warn( + "can't setsockopt IP_MULTICAST_LOOP (0) for fd %d: %s", + top->fd, safe_strerror(errno)); + + /* Explicitly set multicast ttl to 1 -- endo. */ + val = 1; + ret = setsockopt(top->fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&val, + len); + if (ret < 0) + zlog_warn("can't setsockopt IP_MULTICAST_TTL (1) for fd %d: %s", + top->fd, safe_strerror(errno)); + + ret = setsockopt_ipv4_multicast_if(top->fd, p->u.prefix4, ifindex); + if (ret < 0) + zlog_warn( + "can't setsockopt IP_MULTICAST_IF (fd %d, addr %pI4, ifindex %u): %s", + top->fd, &p->u.prefix4, ifindex, safe_strerror(errno)); + + return ret; +} + +/* Join to the EIGRP multicast group. */ +int eigrp_if_add_allspfrouters(struct eigrp *top, struct prefix *p, + unsigned int ifindex) +{ + int ret; + + ret = setsockopt_ipv4_multicast( + top->fd, IP_ADD_MEMBERSHIP, p->u.prefix4, + htonl(EIGRP_MULTICAST_ADDRESS), ifindex); + if (ret < 0) + zlog_warn( + "can't setsockopt IP_ADD_MEMBERSHIP (fd %d, addr %pI4, ifindex %u, AllSPFRouters): %s; perhaps a kernel limit on # of multicast group memberships has been exceeded?", + top->fd, &p->u.prefix4, ifindex, safe_strerror(errno)); + else + zlog_debug("interface %pI4 [%u] join EIGRP Multicast group.", + &p->u.prefix4, ifindex); + + return ret; +} + +int eigrp_if_drop_allspfrouters(struct eigrp *top, struct prefix *p, + unsigned int ifindex) +{ + int ret; + + ret = setsockopt_ipv4_multicast( + top->fd, IP_DROP_MEMBERSHIP, p->u.prefix4, + htonl(EIGRP_MULTICAST_ADDRESS), ifindex); + if (ret < 0) + zlog_warn( + "can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %pI4, ifindex %u, AllSPFRouters): %s", + top->fd, &p->u.prefix4, ifindex, safe_strerror(errno)); + else + zlog_debug("interface %pI4 [%u] leave EIGRP Multicast group.", + &p->u.prefix4, ifindex); + + return ret; +} + +int eigrp_network_set(struct eigrp *eigrp, struct prefix *p) +{ + struct vrf *vrf = vrf_lookup_by_id(eigrp->vrf_id); + struct route_node *rn; + struct interface *ifp; + + rn = route_node_get(eigrp->networks, p); + if (rn->info) { + /* There is already same network statement. */ + route_unlock_node(rn); + return 0; + } + + struct prefix *pref = prefix_new(); + prefix_copy(pref, p); + rn->info = (void *)pref; + + /* Schedule Router ID Update. */ + if (eigrp->router_id.s_addr == INADDR_ANY) + eigrp_router_id_update(eigrp); + /* Run network config now. */ + /* Get target interface. */ + FOR_ALL_INTERFACES (vrf, ifp) { + zlog_debug("Setting up %s", ifp->name); + eigrp_network_run_interface(eigrp, p, ifp); + } + return 1; +} + +/* Check whether interface matches given network + * returns: 1, true. 0, false + */ +static int eigrp_network_match_iface(const struct prefix *co_prefix, + const struct prefix *net) +{ + /* new approach: more elegant and conceptually clean */ + return prefix_match_network_statement(net, co_prefix); +} + +static void eigrp_network_run_interface(struct eigrp *eigrp, struct prefix *p, + struct interface *ifp) +{ + struct eigrp_interface *ei; + struct listnode *cnode; + struct connected *co; + + /* if interface prefix is match specified prefix, + then create socket and join multicast group. */ + for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, co)) { + + if (CHECK_FLAG(co->flags, ZEBRA_IFA_SECONDARY)) + continue; + + if (p->family == co->address->family && !ifp->info + && eigrp_network_match_iface(co->address, p)) { + + ei = eigrp_if_new(eigrp, ifp, co->address); + + /* Relate eigrp interface to eigrp instance. */ + ei->eigrp = eigrp; + + /* if router_id is not configured, dont bring up + * interfaces. + * eigrp_router_id_update() will call eigrp_if_update + * whenever r-id is configured instead. + */ + if (if_is_operative(ifp)) + eigrp_if_up(ei); + } + } +} + +void eigrp_if_update(struct interface *ifp) +{ + struct listnode *node, *nnode; + struct route_node *rn; + struct eigrp *eigrp; + + /* + * In the event there are multiple eigrp autonymnous systems running, + * we need to check eac one and add the interface as approperate + */ + for (ALL_LIST_ELEMENTS(eigrp_om->eigrp, node, nnode, eigrp)) { + if (ifp->vrf->vrf_id != eigrp->vrf_id) + continue; + + /* EIGRP must be on and Router-ID must be configured. */ + if (eigrp->router_id.s_addr == INADDR_ANY) + continue; + + /* Run each network for this interface. */ + for (rn = route_top(eigrp->networks); rn; rn = route_next(rn)) + if (rn->info != NULL) { + eigrp_network_run_interface(eigrp, &rn->p, ifp); + } + } +} + +int eigrp_network_unset(struct eigrp *eigrp, struct prefix *p) +{ + struct route_node *rn; + struct listnode *node, *nnode; + struct eigrp_interface *ei; + struct prefix *pref; + + rn = route_node_lookup(eigrp->networks, p); + if (rn == NULL) + return 0; + + pref = rn->info; + route_unlock_node(rn); + + if (!IPV4_ADDR_SAME(&pref->u.prefix4, &p->u.prefix4)) + return 0; + + prefix_ipv4_free((struct prefix_ipv4 **)&rn->info); + route_unlock_node(rn); /* initial reference */ + + /* Find interfaces that not configured already. */ + for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, ei)) { + bool found = false; + + for (rn = route_top(eigrp->networks); rn; rn = route_next(rn)) { + if (rn->info == NULL) + continue; + + if (eigrp_network_match_iface(&ei->address, &rn->p)) { + found = true; + route_unlock_node(rn); + break; + } + } + + if (!found) { + eigrp_if_free(ei, INTERFACE_DOWN_BY_VTY); + } + } + + return 1; +} + +void eigrp_external_routes_refresh(struct eigrp *eigrp, int type) +{ +} diff --git a/eigrpd/eigrp_network.h b/eigrpd/eigrp_network.h new file mode 100644 index 0000000..ac5c47f --- /dev/null +++ b/eigrpd/eigrp_network.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Network Related Functions. + * Copyright (C) 2013-2014 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + */ + +#ifndef _ZEBRA_EIGRP_NETWORK_H +#define _ZEBRA_EIGRP_NETWORK_H + +/* Prototypes */ + +extern int eigrp_sock_init(struct vrf *vrf); +extern int eigrp_if_ipmulticast(struct eigrp *, struct prefix *, unsigned int); +extern int eigrp_network_set(struct eigrp *eigrp, struct prefix *p); +extern int eigrp_network_unset(struct eigrp *eigrp, struct prefix *p); + +extern void eigrp_hello_timer(struct event *thread); +extern void eigrp_if_update(struct interface *); +extern int eigrp_if_add_allspfrouters(struct eigrp *, struct prefix *, + unsigned int); +extern int eigrp_if_drop_allspfrouters(struct eigrp *top, struct prefix *p, + unsigned int ifindex); +extern void eigrp_adjust_sndbuflen(struct eigrp *, unsigned int); + +extern void eigrp_external_routes_refresh(struct eigrp *, int); + +#endif /* EIGRP_NETWORK_H_ */ diff --git a/eigrpd/eigrp_northbound.c b/eigrpd/eigrp_northbound.c new file mode 100644 index 0000000..f50abb7 --- /dev/null +++ b/eigrpd/eigrp_northbound.c @@ -0,0 +1,1523 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP daemon northbound implementation. + * + * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF") + * Rafael Zalamena + */ + +#include <zebra.h> + +#include "lib/keychain.h" +#include "lib/log.h" +#include "lib/northbound.h" +#include "lib/table.h" +#include "lib/vrf.h" +#include "lib/zclient.h" + +#include "eigrp_structs.h" +#include "eigrpd.h" +#include "eigrp_interface.h" +#include "eigrp_network.h" +#include "eigrp_zebra.h" +#include "eigrp_cli.h" + +/* Helper functions. */ +static void redistribute_get_metrics(const struct lyd_node *dnode, + struct eigrp_metrics *em) +{ + memset(em, 0, sizeof(*em)); + + if (yang_dnode_exists(dnode, "./bandwidth")) + em->bandwidth = yang_dnode_get_uint32(dnode, "./bandwidth"); + if (yang_dnode_exists(dnode, "./delay")) + em->delay = yang_dnode_get_uint32(dnode, "./delay"); +#if 0 /* TODO: How does MTU work? */ + if (yang_dnode_exists(dnode, "./mtu")) + em->mtu[0] = yang_dnode_get_uint32(dnode, "./mtu"); +#endif + if (yang_dnode_exists(dnode, "./load")) + em->load = yang_dnode_get_uint32(dnode, "./load"); + if (yang_dnode_exists(dnode, "./reliability")) + em->reliability = yang_dnode_get_uint32(dnode, "./reliability"); +} + +static struct eigrp_interface *eigrp_interface_lookup(const struct eigrp *eigrp, + const char *ifname) +{ + struct eigrp_interface *eif; + struct listnode *ln; + + for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, ln, eif)) { + if (strcmp(ifname, eif->ifp->name)) + continue; + + return eif; + } + + return NULL; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance + */ +static int eigrpd_instance_create(struct nb_cb_create_args *args) +{ + struct eigrp *eigrp; + const char *vrf; + struct vrf *pVrf; + vrf_id_t vrfid; + + switch (args->event) { + case NB_EV_VALIDATE: + /* NOTHING */ + break; + case NB_EV_PREPARE: + vrf = yang_dnode_get_string(args->dnode, "./vrf"); + + pVrf = vrf_lookup_by_name(vrf); + if (pVrf) + vrfid = pVrf->vrf_id; + else + vrfid = VRF_DEFAULT; + + eigrp = eigrp_get(yang_dnode_get_uint16(args->dnode, "./asn"), + vrfid); + args->resource->ptr = eigrp; + break; + case NB_EV_ABORT: + eigrp_finish_final(args->resource->ptr); + break; + case NB_EV_APPLY: + nb_running_set_entry(args->dnode, args->resource->ptr); + break; + } + + return NB_OK; +} + +static int eigrpd_instance_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_unset_entry(args->dnode); + eigrp_finish_final(eigrp); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/router-id + */ +static int eigrpd_instance_router_id_modify(struct nb_cb_modify_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + yang_dnode_get_ipv4(&eigrp->router_id_static, args->dnode, + NULL); + break; + } + + return NB_OK; +} + +static int eigrpd_instance_router_id_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->router_id_static.s_addr = INADDR_ANY; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/passive-interface + */ +static int +eigrpd_instance_passive_interface_create(struct nb_cb_create_args *args) +{ + struct eigrp_interface *eif; + struct eigrp *eigrp; + const char *ifname; + + switch (args->event) { + case NB_EV_VALIDATE: + eigrp = nb_running_get_entry(args->dnode, NULL, false); + if (eigrp == NULL) { + /* + * XXX: we can't verify if the interface exists + * and is active until EIGRP is up. + */ + break; + } + + ifname = yang_dnode_get_string(args->dnode, NULL); + eif = eigrp_interface_lookup(eigrp, ifname); + if (eif == NULL) + return NB_ERR_INCONSISTENCY; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + ifname = yang_dnode_get_string(args->dnode, NULL); + eif = eigrp_interface_lookup(eigrp, ifname); + if (eif == NULL) + return NB_ERR_INCONSISTENCY; + + eif->params.passive_interface = EIGRP_IF_PASSIVE; + break; + } + + return NB_OK; +} + +static int +eigrpd_instance_passive_interface_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp_interface *eif; + struct eigrp *eigrp; + const char *ifname; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + ifname = yang_dnode_get_string(args->dnode, NULL); + eif = eigrp_interface_lookup(eigrp, ifname); + if (eif == NULL) + break; + + eif->params.passive_interface = EIGRP_IF_ACTIVE; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/active-time + */ +static int eigrpd_instance_active_time_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + /* TODO: Not implemented. */ + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + snprintf(args->errmsg, args->errmsg_len, + "active time not implemented yet"); + /* NOTHING */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/variance + */ +static int eigrpd_instance_variance_modify(struct nb_cb_modify_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->variance = yang_dnode_get_uint8(args->dnode, NULL); + break; + } + + return NB_OK; +} + +static int eigrpd_instance_variance_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->variance = EIGRP_VARIANCE_DEFAULT; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/maximum-paths + */ +static int eigrpd_instance_maximum_paths_modify(struct nb_cb_modify_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->max_paths = yang_dnode_get_uint8(args->dnode, NULL); + break; + } + + return NB_OK; +} + +static int +eigrpd_instance_maximum_paths_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->max_paths = EIGRP_MAX_PATHS_DEFAULT; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K1 + */ +static int +eigrpd_instance_metric_weights_K1_modify(struct nb_cb_modify_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[0] = yang_dnode_get_uint8(args->dnode, NULL); + break; + } + + return NB_OK; +} + +static int +eigrpd_instance_metric_weights_K1_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[0] = EIGRP_K1_DEFAULT; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K2 + */ +static int +eigrpd_instance_metric_weights_K2_modify(struct nb_cb_modify_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[1] = yang_dnode_get_uint8(args->dnode, NULL); + break; + } + + return NB_OK; +} + +static int +eigrpd_instance_metric_weights_K2_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[1] = EIGRP_K2_DEFAULT; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K3 + */ +static int +eigrpd_instance_metric_weights_K3_modify(struct nb_cb_modify_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[2] = yang_dnode_get_uint8(args->dnode, NULL); + break; + } + + return NB_OK; +} + +static int +eigrpd_instance_metric_weights_K3_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[2] = EIGRP_K3_DEFAULT; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K4 + */ +static int +eigrpd_instance_metric_weights_K4_modify(struct nb_cb_modify_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[3] = yang_dnode_get_uint8(args->dnode, NULL); + break; + } + + return NB_OK; +} + +static int +eigrpd_instance_metric_weights_K4_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[3] = EIGRP_K4_DEFAULT; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K5 + */ +static int +eigrpd_instance_metric_weights_K5_modify(struct nb_cb_modify_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[4] = yang_dnode_get_uint8(args->dnode, NULL); + break; + } + + return NB_OK; +} + +static int +eigrpd_instance_metric_weights_K5_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[4] = EIGRP_K5_DEFAULT; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K6 + */ +static int +eigrpd_instance_metric_weights_K6_modify(struct nb_cb_modify_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[5] = yang_dnode_get_uint8(args->dnode, NULL); + break; + } + + return NB_OK; +} + +static int +eigrpd_instance_metric_weights_K6_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[5] = EIGRP_K6_DEFAULT; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/network + */ +static int eigrpd_instance_network_create(struct nb_cb_create_args *args) +{ + struct route_node *rnode; + struct prefix prefix; + struct eigrp *eigrp; + int exists; + + yang_dnode_get_ipv4p(&prefix, args->dnode, NULL); + + switch (args->event) { + case NB_EV_VALIDATE: + eigrp = nb_running_get_entry(args->dnode, NULL, false); + /* If entry doesn't exist it means the list is empty. */ + if (eigrp == NULL) + break; + + rnode = route_node_get(eigrp->networks, &prefix); + exists = (rnode->info != NULL); + route_unlock_node(rnode); + if (exists) + return NB_ERR_INCONSISTENCY; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + if (eigrp_network_set(eigrp, &prefix) == 0) + return NB_ERR_INCONSISTENCY; + break; + } + + return NB_OK; +} + +static int eigrpd_instance_network_destroy(struct nb_cb_destroy_args *args) +{ + struct route_node *rnode; + struct prefix prefix; + struct eigrp *eigrp; + int exists = 0; + + yang_dnode_get_ipv4p(&prefix, args->dnode, NULL); + + switch (args->event) { + case NB_EV_VALIDATE: + eigrp = nb_running_get_entry(args->dnode, NULL, false); + /* If entry doesn't exist it means the list is empty. */ + if (eigrp == NULL) + break; + + rnode = route_node_get(eigrp->networks, &prefix); + exists = (rnode->info != NULL); + route_unlock_node(rnode); + if (exists == 0) + return NB_ERR_INCONSISTENCY; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp_network_unset(eigrp, &prefix); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/neighbor + */ +static int eigrpd_instance_neighbor_create(struct nb_cb_create_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + /* TODO: Not implemented. */ + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + snprintf(args->errmsg, args->errmsg_len, + "neighbor Command is not implemented yet"); + break; + } + + return NB_OK; +} + +static int eigrpd_instance_neighbor_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + /* TODO: Not implemented. */ + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + snprintf(args->errmsg, args->errmsg_len, + "no neighbor Command is not implemented yet"); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/redistribute + */ +static int eigrpd_instance_redistribute_create(struct nb_cb_create_args *args) +{ + struct eigrp_metrics metrics; + const char *vrfname; + struct eigrp *eigrp; + uint32_t proto; + vrf_id_t vrfid; + struct vrf *pVrf; + + switch (args->event) { + case NB_EV_VALIDATE: + proto = yang_dnode_get_enum(args->dnode, "./protocol"); + vrfname = yang_dnode_get_string(args->dnode, "../vrf"); + + pVrf = vrf_lookup_by_name(vrfname); + if (pVrf) + vrfid = pVrf->vrf_id; + else + vrfid = VRF_DEFAULT; + + if (vrf_bitmap_check(&zclient->redist[AFI_IP][proto], vrfid)) + return NB_ERR_INCONSISTENCY; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + proto = yang_dnode_get_enum(args->dnode, "./protocol"); + redistribute_get_metrics(args->dnode, &metrics); + eigrp_redistribute_set(eigrp, proto, metrics); + break; + } + + return NB_OK; +} + +static int eigrpd_instance_redistribute_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + uint32_t proto; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + proto = yang_dnode_get_enum(args->dnode, "./protocol"); + eigrp_redistribute_unset(eigrp, proto); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/route-map + */ +static int +eigrpd_instance_redistribute_route_map_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + /* TODO: Not implemented. */ + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + snprintf( + args->errmsg, args->errmsg_len, + "'redistribute X route-map FOO' command not implemented yet"); + break; + } + + return NB_OK; +} + +static int +eigrpd_instance_redistribute_route_map_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + /* TODO: Not implemented. */ + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + snprintf( + args->errmsg, args->errmsg_len, + "'no redistribute X route-map FOO' command not implemented yet"); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/bandwidth + */ +static int eigrpd_instance_redistribute_metrics_bandwidth_modify( + struct nb_cb_modify_args *args) +{ + struct eigrp_metrics metrics; + struct eigrp *eigrp; + uint32_t proto; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + proto = yang_dnode_get_enum(args->dnode, "../../protocol"); + redistribute_get_metrics(args->dnode, &metrics); + eigrp_redistribute_set(eigrp, proto, metrics); + break; + } + + return NB_OK; +} + +static int eigrpd_instance_redistribute_metrics_bandwidth_destroy( + struct nb_cb_destroy_args *args) +{ + struct eigrp_metrics metrics; + struct eigrp *eigrp; + uint32_t proto; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + proto = yang_dnode_get_enum(args->dnode, "../../protocol"); + redistribute_get_metrics(args->dnode, &metrics); + eigrp_redistribute_set(eigrp, proto, metrics); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/delay + */ +static int eigrpd_instance_redistribute_metrics_delay_modify( + struct nb_cb_modify_args *args) +{ + return eigrpd_instance_redistribute_metrics_bandwidth_modify(args); +} + +static int eigrpd_instance_redistribute_metrics_delay_destroy( + struct nb_cb_destroy_args *args) +{ + return eigrpd_instance_redistribute_metrics_bandwidth_destroy(args); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/reliability + */ +static int eigrpd_instance_redistribute_metrics_reliability_modify( + struct nb_cb_modify_args *args) +{ + return eigrpd_instance_redistribute_metrics_bandwidth_modify(args); +} + +static int eigrpd_instance_redistribute_metrics_reliability_destroy( + struct nb_cb_destroy_args *args) +{ + return eigrpd_instance_redistribute_metrics_bandwidth_destroy(args); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/load + */ +static int +eigrpd_instance_redistribute_metrics_load_modify(struct nb_cb_modify_args *args) +{ + return eigrpd_instance_redistribute_metrics_bandwidth_modify(args); +} + +static int eigrpd_instance_redistribute_metrics_load_destroy( + struct nb_cb_destroy_args *args) +{ + return eigrpd_instance_redistribute_metrics_bandwidth_destroy(args); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/mtu + */ +static int +eigrpd_instance_redistribute_metrics_mtu_modify(struct nb_cb_modify_args *args) +{ + return eigrpd_instance_redistribute_metrics_bandwidth_modify(args); +} + +static int eigrpd_instance_redistribute_metrics_mtu_destroy( + struct nb_cb_destroy_args *args) +{ + return eigrpd_instance_redistribute_metrics_bandwidth_destroy(args); +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/delay + */ +static int lib_interface_eigrp_delay_modify(struct nb_cb_modify_args *args) +{ + struct eigrp_interface *ei; + struct interface *ifp; + + switch (args->event) { + case NB_EV_VALIDATE: + ifp = nb_running_get_entry(args->dnode, NULL, false); + if (ifp == NULL) { + /* + * XXX: we can't verify if the interface exists + * and is active until EIGRP is up. + */ + break; + } + + ei = ifp->info; + if (ei == NULL) + return NB_ERR_INCONSISTENCY; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + ei = ifp->info; + if (ei == NULL) + return NB_ERR_INCONSISTENCY; + + ei->params.delay = yang_dnode_get_uint32(args->dnode, NULL); + eigrp_if_reset(ifp); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/bandwidth + */ +static int lib_interface_eigrp_bandwidth_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct eigrp_interface *ei; + + switch (args->event) { + case NB_EV_VALIDATE: + ifp = nb_running_get_entry(args->dnode, NULL, false); + if (ifp == NULL) { + /* + * XXX: we can't verify if the interface exists + * and is active until EIGRP is up. + */ + break; + } + + ei = ifp->info; + if (ei == NULL) + return NB_ERR_INCONSISTENCY; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + ei = ifp->info; + if (ei == NULL) + return NB_ERR_INCONSISTENCY; + + ei->params.bandwidth = yang_dnode_get_uint32(args->dnode, NULL); + eigrp_if_reset(ifp); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/hello-interval + */ +static int +lib_interface_eigrp_hello_interval_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct eigrp_interface *ei; + + switch (args->event) { + case NB_EV_VALIDATE: + ifp = nb_running_get_entry(args->dnode, NULL, false); + if (ifp == NULL) { + /* + * XXX: we can't verify if the interface exists + * and is active until EIGRP is up. + */ + break; + } + + ei = ifp->info; + if (ei == NULL) + return NB_ERR_INCONSISTENCY; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + ei = ifp->info; + if (ei == NULL) + return NB_ERR_INCONSISTENCY; + + ei->params.v_hello = yang_dnode_get_uint16(args->dnode, NULL); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/hold-time + */ +static int lib_interface_eigrp_hold_time_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct eigrp_interface *ei; + + switch (args->event) { + case NB_EV_VALIDATE: + ifp = nb_running_get_entry(args->dnode, NULL, false); + if (ifp == NULL) { + /* + * XXX: we can't verify if the interface exists + * and is active until EIGRP is up. + */ + break; + } + + ei = ifp->info; + if (ei == NULL) + return NB_ERR_INCONSISTENCY; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + ei = ifp->info; + if (ei == NULL) + return NB_ERR_INCONSISTENCY; + + ei->params.v_wait = yang_dnode_get_uint16(args->dnode, NULL); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/split-horizon + */ +static int +lib_interface_eigrp_split_horizon_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + /* TODO: Not implemented. */ + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + snprintf(args->errmsg, args->errmsg_len, + "split-horizon command not implemented yet"); + /* NOTHING */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance + */ +static int lib_interface_eigrp_instance_create(struct nb_cb_create_args *args) +{ + struct eigrp_interface *eif; + struct interface *ifp; + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + ifp = nb_running_get_entry(args->dnode, NULL, false); + if (ifp == NULL) { + /* + * XXX: we can't verify if the interface exists + * and is active until EIGRP is up. + */ + break; + } + + eigrp = eigrp_get(yang_dnode_get_uint16(args->dnode, "./asn"), + ifp->vrf->vrf_id); + eif = eigrp_interface_lookup(eigrp, ifp->name); + if (eif == NULL) + return NB_ERR_INCONSISTENCY; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + eigrp = eigrp_get(yang_dnode_get_uint16(args->dnode, "./asn"), + ifp->vrf->vrf_id); + eif = eigrp_interface_lookup(eigrp, ifp->name); + if (eif == NULL) + return NB_ERR_INCONSISTENCY; + + nb_running_set_entry(args->dnode, eif); + break; + } + + return NB_OK; +} + +static int lib_interface_eigrp_instance_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + nb_running_unset_entry(args->dnode); + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/summarize-addresses + */ +static int lib_interface_eigrp_instance_summarize_addresses_create( + struct nb_cb_create_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + /* TODO: Not implemented. */ + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + snprintf(args->errmsg, args->errmsg_len, + "summary command not implemented yet"); + break; + } + + return NB_OK; +} + +static int lib_interface_eigrp_instance_summarize_addresses_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + /* TODO: Not implemented. */ + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + snprintf(args->errmsg, args->errmsg_len, + "no summary command not implemented yet"); + /* NOTHING */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/authentication + */ +static int lib_interface_eigrp_instance_authentication_modify( + struct nb_cb_modify_args *args) +{ + struct eigrp_interface *eif; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eif = nb_running_get_entry(args->dnode, NULL, true); + eif->params.auth_type = yang_dnode_get_enum(args->dnode, NULL); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/keychain + */ +static int +lib_interface_eigrp_instance_keychain_modify(struct nb_cb_modify_args *args) +{ + struct eigrp_interface *eif; + struct keychain *keychain; + + switch (args->event) { + case NB_EV_VALIDATE: + keychain = keychain_lookup( + yang_dnode_get_string(args->dnode, NULL)); + if (keychain == NULL) + return NB_ERR_INCONSISTENCY; + break; + case NB_EV_PREPARE: + args->resource->ptr = + strdup(yang_dnode_get_string(args->dnode, NULL)); + if (args->resource->ptr == NULL) + return NB_ERR_RESOURCE; + break; + case NB_EV_ABORT: + free(args->resource->ptr); + args->resource->ptr = NULL; + break; + case NB_EV_APPLY: + eif = nb_running_get_entry(args->dnode, NULL, true); + if (eif->params.auth_keychain) + free(eif->params.auth_keychain); + + eif->params.auth_keychain = args->resource->ptr; + break; + } + + return NB_OK; +} + +static int +lib_interface_eigrp_instance_keychain_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp_interface *eif; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eif = nb_running_get_entry(args->dnode, NULL, true); + if (eif->params.auth_keychain) + free(eif->params.auth_keychain); + + eif->params.auth_keychain = NULL; + break; + } + + return NB_OK; +} + +/* clang-format off */ +const struct frr_yang_module_info frr_eigrpd_info = { + .name = "frr-eigrpd", + .nodes = { + { + .xpath = "/frr-eigrpd:eigrpd/instance", + .cbs = { + .create = eigrpd_instance_create, + .destroy = eigrpd_instance_destroy, + .cli_show = eigrp_cli_show_header, + .cli_show_end = eigrp_cli_show_end_header, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/router-id", + .cbs = { + .modify = eigrpd_instance_router_id_modify, + .destroy = eigrpd_instance_router_id_destroy, + .cli_show = eigrp_cli_show_router_id, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/passive-interface", + .cbs = { + .create = eigrpd_instance_passive_interface_create, + .destroy = eigrpd_instance_passive_interface_destroy, + .cli_show = eigrp_cli_show_passive_interface, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/active-time", + .cbs = { + .modify = eigrpd_instance_active_time_modify, + .cli_show = eigrp_cli_show_active_time, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/variance", + .cbs = { + .modify = eigrpd_instance_variance_modify, + .destroy = eigrpd_instance_variance_destroy, + .cli_show = eigrp_cli_show_variance, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/maximum-paths", + .cbs = { + .modify = eigrpd_instance_maximum_paths_modify, + .destroy = eigrpd_instance_maximum_paths_destroy, + .cli_show = eigrp_cli_show_maximum_paths, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights", + .cbs = { + .cli_show = eigrp_cli_show_metrics, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights/K1", + .cbs = { + .modify = eigrpd_instance_metric_weights_K1_modify, + .destroy = eigrpd_instance_metric_weights_K1_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights/K2", + .cbs = { + .modify = eigrpd_instance_metric_weights_K2_modify, + .destroy = eigrpd_instance_metric_weights_K2_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights/K3", + .cbs = { + .modify = eigrpd_instance_metric_weights_K3_modify, + .destroy = eigrpd_instance_metric_weights_K3_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights/K4", + .cbs = { + .modify = eigrpd_instance_metric_weights_K4_modify, + .destroy = eigrpd_instance_metric_weights_K4_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights/K5", + .cbs = { + .modify = eigrpd_instance_metric_weights_K5_modify, + .destroy = eigrpd_instance_metric_weights_K5_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights/K6", + .cbs = { + .modify = eigrpd_instance_metric_weights_K6_modify, + .destroy = eigrpd_instance_metric_weights_K6_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/network", + .cbs = { + .create = eigrpd_instance_network_create, + .destroy = eigrpd_instance_network_destroy, + .cli_show = eigrp_cli_show_network, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/neighbor", + .cbs = { + .create = eigrpd_instance_neighbor_create, + .destroy = eigrpd_instance_neighbor_destroy, + .cli_show = eigrp_cli_show_neighbor, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/redistribute", + .cbs = { + .create = eigrpd_instance_redistribute_create, + .destroy = eigrpd_instance_redistribute_destroy, + .cli_show = eigrp_cli_show_redistribute, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/redistribute/route-map", + .cbs = { + .modify = eigrpd_instance_redistribute_route_map_modify, + .destroy = eigrpd_instance_redistribute_route_map_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/redistribute/metrics/bandwidth", + .cbs = { + .modify = eigrpd_instance_redistribute_metrics_bandwidth_modify, + .destroy = eigrpd_instance_redistribute_metrics_bandwidth_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/redistribute/metrics/delay", + .cbs = { + .modify = eigrpd_instance_redistribute_metrics_delay_modify, + .destroy = eigrpd_instance_redistribute_metrics_delay_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/redistribute/metrics/reliability", + .cbs = { + .modify = eigrpd_instance_redistribute_metrics_reliability_modify, + .destroy = eigrpd_instance_redistribute_metrics_reliability_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/redistribute/metrics/load", + .cbs = { + .modify = eigrpd_instance_redistribute_metrics_load_modify, + .destroy = eigrpd_instance_redistribute_metrics_load_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/redistribute/metrics/mtu", + .cbs = { + .modify = eigrpd_instance_redistribute_metrics_mtu_modify, + .destroy = eigrpd_instance_redistribute_metrics_mtu_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/delay", + .cbs = { + .modify = lib_interface_eigrp_delay_modify, + .cli_show = eigrp_cli_show_delay, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/bandwidth", + .cbs = { + .modify = lib_interface_eigrp_bandwidth_modify, + .cli_show = eigrp_cli_show_bandwidth, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/hello-interval", + .cbs = { + .modify = lib_interface_eigrp_hello_interval_modify, + .cli_show = eigrp_cli_show_hello_interval, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/hold-time", + .cbs = { + .modify = lib_interface_eigrp_hold_time_modify, + .cli_show = eigrp_cli_show_hold_time, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/split-horizon", + .cbs = { + .modify = lib_interface_eigrp_split_horizon_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/instance", + .cbs = { + .create = lib_interface_eigrp_instance_create, + .destroy = lib_interface_eigrp_instance_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/instance/summarize-addresses", + .cbs = { + .create = lib_interface_eigrp_instance_summarize_addresses_create, + .destroy = lib_interface_eigrp_instance_summarize_addresses_destroy, + .cli_show = eigrp_cli_show_summarize_address, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/instance/authentication", + .cbs = { + .modify = lib_interface_eigrp_instance_authentication_modify, + .cli_show = eigrp_cli_show_authentication, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/instance/keychain", + .cbs = { + .modify = lib_interface_eigrp_instance_keychain_modify, + .destroy = lib_interface_eigrp_instance_keychain_destroy, + .cli_show = eigrp_cli_show_keychain, + } + }, + { + .xpath = NULL, + }, + } +}; diff --git a/eigrpd/eigrp_packet.c b/eigrpd/eigrp_packet.c new file mode 100644 index 0000000..963d229 --- /dev/null +++ b/eigrpd/eigrp_packet.c @@ -0,0 +1,1333 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP General Sending and Receiving of EIGRP Packets. + * Copyright (C) 2013-2014 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + */ + +#include <zebra.h> + +#include "frrevent.h" +#include "memory.h" +#include "linklist.h" +#include "vty.h" +#include "keychain.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "sockunion.h" +#include "stream.h" +#include "log.h" +#include "sockopt.h" +#include "checksum.h" +#include "md5.h" +#include "sha256.h" +#include "lib_errors.h" + +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_interface.h" +#include "eigrpd/eigrp_neighbor.h" +#include "eigrpd/eigrp_packet.h" +#include "eigrpd/eigrp_zebra.h" +#include "eigrpd/eigrp_vty.h" +#include "eigrpd/eigrp_dump.h" +#include "eigrpd/eigrp_macros.h" +#include "eigrpd/eigrp_network.h" +#include "eigrpd/eigrp_topology.h" +#include "eigrpd/eigrp_fsm.h" +#include "eigrpd/eigrp_errors.h" + +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_FIFO, "EIGRP FIFO"); +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_PACKET, "EIGRP Packet"); +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_IPV4_INT_TLV, "EIGRP IPv4 TLV"); +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_SEQ_TLV, "EIGRP SEQ TLV"); +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_AUTH_TLV, "EIGRP AUTH TLV"); +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_AUTH_SHA256_TLV, "EIGRP SHA TLV"); + +/* Packet Type String. */ +const struct message eigrp_packet_type_str[] = { + {EIGRP_OPC_UPDATE, "Update"}, + {EIGRP_OPC_REQUEST, "Request"}, + {EIGRP_OPC_QUERY, "Query"}, + {EIGRP_OPC_REPLY, "Reply"}, + {EIGRP_OPC_HELLO, "Hello"}, + {EIGRP_OPC_IPXSAP, "IPX-SAP"}, + {EIGRP_OPC_PROBE, "Probe"}, + {EIGRP_OPC_ACK, "Ack"}, + {EIGRP_OPC_SIAQUERY, "SIAQuery"}, + {EIGRP_OPC_SIAREPLY, "SIAReply"}, + {0}}; + + +static unsigned char zeropad[16] = {0}; + +/* Forward function reference*/ +static struct stream *eigrp_recv_packet(struct eigrp *eigrp, int fd, + struct interface **ifp, + struct stream *s); +static int eigrp_verify_header(struct stream *s, struct eigrp_interface *ei, + struct ip *addr, struct eigrp_header *header); +static int eigrp_check_network_mask(struct eigrp_interface *ei, + struct in_addr mask); + +static int eigrp_retrans_count_exceeded(struct eigrp_packet *ep, + struct eigrp_neighbor *nbr) +{ + return 1; +} + +int eigrp_make_md5_digest(struct eigrp_interface *ei, struct stream *s, + uint8_t flags) +{ + struct key *key = NULL; + struct keychain *keychain; + + unsigned char digest[EIGRP_AUTH_TYPE_MD5_LEN]; + MD5_CTX ctx; + uint8_t *ibuf; + size_t backup_get, backup_end; + struct TLV_MD5_Authentication_Type *auth_TLV; + + ibuf = s->data; + backup_end = s->endp; + backup_get = s->getp; + + auth_TLV = eigrp_authTLV_MD5_new(); + + stream_set_getp(s, EIGRP_HEADER_LEN); + stream_get(auth_TLV, s, EIGRP_AUTH_MD5_TLV_SIZE); + stream_set_getp(s, backup_get); + + keychain = keychain_lookup(ei->params.auth_keychain); + if (keychain) + key = key_lookup_for_send(keychain); + else { + eigrp_authTLV_MD5_free(auth_TLV); + return EIGRP_AUTH_TYPE_NONE; + } + + memset(&ctx, 0, sizeof(ctx)); + MD5Init(&ctx); + + /* Generate a digest. Each situation needs different handling */ + if (flags & EIGRP_AUTH_BASIC_HELLO_FLAG) { + MD5Update(&ctx, ibuf, EIGRP_MD5_BASIC_COMPUTE); + MD5Update(&ctx, key->string, strlen(key->string)); + if (strlen(key->string) < 16) + MD5Update(&ctx, zeropad, 16 - strlen(key->string)); + } else if (flags & EIGRP_AUTH_UPDATE_INIT_FLAG) { + MD5Update(&ctx, ibuf, EIGRP_MD5_UPDATE_INIT_COMPUTE); + } else if (flags & EIGRP_AUTH_UPDATE_FLAG) { + MD5Update(&ctx, ibuf, EIGRP_MD5_BASIC_COMPUTE); + MD5Update(&ctx, key->string, strlen(key->string)); + if (strlen(key->string) < 16) + MD5Update(&ctx, zeropad, 16 - strlen(key->string)); + if (backup_end > (EIGRP_HEADER_LEN + EIGRP_AUTH_MD5_TLV_SIZE)) { + MD5Update(&ctx, + ibuf + (EIGRP_HEADER_LEN + + EIGRP_AUTH_MD5_TLV_SIZE), + backup_end - 20 + - (EIGRP_HEADER_LEN + + EIGRP_AUTH_MD5_TLV_SIZE)); + } + } + + MD5Final(digest, &ctx); + + /* Append md5 digest to the end of the stream. */ + memcpy(auth_TLV->digest, digest, EIGRP_AUTH_TYPE_MD5_LEN); + + stream_set_endp(s, EIGRP_HEADER_LEN); + stream_put(s, auth_TLV, EIGRP_AUTH_MD5_TLV_SIZE); + stream_set_endp(s, backup_end); + + eigrp_authTLV_MD5_free(auth_TLV); + return EIGRP_AUTH_TYPE_MD5_LEN; +} + +int eigrp_check_md5_digest(struct stream *s, + struct TLV_MD5_Authentication_Type *authTLV, + struct eigrp_neighbor *nbr, uint8_t flags) +{ + MD5_CTX ctx; + unsigned char digest[EIGRP_AUTH_TYPE_MD5_LEN]; + unsigned char orig[EIGRP_AUTH_TYPE_MD5_LEN]; + struct key *key = NULL; + struct keychain *keychain; + uint8_t *ibuf; + size_t backup_end; + struct TLV_MD5_Authentication_Type *auth_TLV; + struct eigrp_header *eigrph; + + if (ntohl(nbr->crypt_seqnum) > ntohl(authTLV->key_sequence)) { + zlog_warn( + "interface %s: eigrp_check_md5 bad sequence %d (expect %d)", + IF_NAME(nbr->ei), ntohl(authTLV->key_sequence), + ntohl(nbr->crypt_seqnum)); + return 0; + } + + eigrph = (struct eigrp_header *)s->data; + eigrph->checksum = 0; + + auth_TLV = (struct TLV_MD5_Authentication_Type *)(s->data + + EIGRP_HEADER_LEN); + memcpy(orig, auth_TLV->digest, EIGRP_AUTH_TYPE_MD5_LEN); + memset(digest, 0, EIGRP_AUTH_TYPE_MD5_LEN); + memset(auth_TLV->digest, 0, EIGRP_AUTH_TYPE_MD5_LEN); + + ibuf = s->data; + backup_end = s->endp; + + keychain = keychain_lookup(nbr->ei->params.auth_keychain); + if (keychain) + key = key_lookup_for_send(keychain); + + if (!key) { + zlog_warn( + "Interface %s: Expected key value not found in config", + nbr->ei->ifp->name); + return 0; + } + + memset(&ctx, 0, sizeof(ctx)); + MD5Init(&ctx); + + /* Generate a digest. Each situation needs different handling */ + if (flags & EIGRP_AUTH_BASIC_HELLO_FLAG) { + MD5Update(&ctx, ibuf, EIGRP_MD5_BASIC_COMPUTE); + MD5Update(&ctx, key->string, strlen(key->string)); + if (strlen(key->string) < 16) + MD5Update(&ctx, zeropad, 16 - strlen(key->string)); + } else if (flags & EIGRP_AUTH_UPDATE_INIT_FLAG) { + MD5Update(&ctx, ibuf, EIGRP_MD5_UPDATE_INIT_COMPUTE); + } else if (flags & EIGRP_AUTH_UPDATE_FLAG) { + MD5Update(&ctx, ibuf, EIGRP_MD5_BASIC_COMPUTE); + MD5Update(&ctx, key->string, strlen(key->string)); + if (strlen(key->string) < 16) + MD5Update(&ctx, zeropad, 16 - strlen(key->string)); + if (backup_end > (EIGRP_HEADER_LEN + EIGRP_AUTH_MD5_TLV_SIZE)) { + MD5Update(&ctx, + ibuf + (EIGRP_HEADER_LEN + + EIGRP_AUTH_MD5_TLV_SIZE), + backup_end - 20 + - (EIGRP_HEADER_LEN + + EIGRP_AUTH_MD5_TLV_SIZE)); + } + } + + MD5Final(digest, &ctx); + + /* compare the two */ + if (memcmp(orig, digest, EIGRP_AUTH_TYPE_MD5_LEN) != 0) { + zlog_warn("interface %s: eigrp_check_md5 checksum mismatch", + IF_NAME(nbr->ei)); + return 0; + } + + /* save neighbor's crypt_seqnum */ + nbr->crypt_seqnum = authTLV->key_sequence; + + return 1; +} + +int eigrp_make_sha256_digest(struct eigrp_interface *ei, struct stream *s, + uint8_t flags) +{ + struct key *key = NULL; + struct keychain *keychain; + char source_ip[PREFIX_STRLEN]; + + unsigned char digest[EIGRP_AUTH_TYPE_SHA256_LEN]; + unsigned char buffer[1 + PLAINTEXT_LENGTH + 45 + 1] = {0}; + + HMAC_SHA256_CTX ctx; + void *ibuf; + size_t backup_get, backup_end; + struct TLV_SHA256_Authentication_Type *auth_TLV; + + ibuf = s->data; + backup_end = s->endp; + backup_get = s->getp; + + auth_TLV = eigrp_authTLV_SHA256_new(); + + stream_set_getp(s, EIGRP_HEADER_LEN); + stream_get(auth_TLV, s, EIGRP_AUTH_SHA256_TLV_SIZE); + stream_set_getp(s, backup_get); + + keychain = keychain_lookup(ei->params.auth_keychain); + if (keychain) + key = key_lookup_for_send(keychain); + + if (!key) { + zlog_warn( + "Interface %s: Expected key value not found in config", + ei->ifp->name); + eigrp_authTLV_SHA256_free(auth_TLV); + return 0; + } + + inet_ntop(AF_INET, &ei->address.u.prefix4, source_ip, PREFIX_STRLEN); + + memset(&ctx, 0, sizeof(ctx)); + buffer[0] = '\n'; + memcpy(buffer + 1, key, strlen(key->string)); + memcpy(buffer + 1 + strlen(key->string), source_ip, strlen(source_ip)); + HMAC__SHA256_Init(&ctx, buffer, + 1 + strlen(key->string) + strlen(source_ip)); + HMAC__SHA256_Update(&ctx, ibuf, strlen(ibuf)); + HMAC__SHA256_Final(digest, &ctx); + + + /* Put hmac-sha256 digest to it's place */ + memcpy(auth_TLV->digest, digest, EIGRP_AUTH_TYPE_SHA256_LEN); + + stream_set_endp(s, EIGRP_HEADER_LEN); + stream_put(s, auth_TLV, EIGRP_AUTH_SHA256_TLV_SIZE); + stream_set_endp(s, backup_end); + + eigrp_authTLV_SHA256_free(auth_TLV); + + return EIGRP_AUTH_TYPE_SHA256_LEN; +} + +int eigrp_check_sha256_digest(struct stream *s, + struct TLV_SHA256_Authentication_Type *authTLV, + struct eigrp_neighbor *nbr, uint8_t flags) +{ + return 1; +} + +void eigrp_write(struct event *thread) +{ + struct eigrp *eigrp = EVENT_ARG(thread); + struct eigrp_header *eigrph; + struct eigrp_interface *ei; + struct eigrp_packet *ep; + struct sockaddr_in sa_dst; + struct ip iph; + struct msghdr msg; + struct iovec iov[2]; + uint32_t seqno, ack; + + int ret; + int flags = 0; + struct listnode *node; +#ifdef WANT_EIGRP_WRITE_FRAGMENT + static uint16_t ipid = 0; +#endif /* WANT_EIGRP_WRITE_FRAGMENT */ +#define EIGRP_WRITE_IPHL_SHIFT 2 + + node = listhead(eigrp->oi_write_q); + assert(node); + ei = listgetdata(node); + assert(ei); + +#ifdef WANT_EIGRP_WRITE_FRAGMENT + /* seed ipid static with low order bits of time */ + if (ipid == 0) + ipid = (time(NULL) & 0xffff); +#endif /* WANT_EIGRP_WRITE_FRAGMENT */ + + /* Get one packet from queue. */ + ep = eigrp_fifo_next(ei->obuf); + if (!ep) { + flog_err(EC_LIB_DEVELOPMENT, + "%s: Interface %s no packet on queue?", __func__, + ei->ifp->name); + goto out; + } + if (ep->length < EIGRP_HEADER_LEN) { + flog_err(EC_EIGRP_PACKET, "%s: Packet just has a header?", + __func__); + eigrp_header_dump((struct eigrp_header *)ep->s->data); + eigrp_packet_delete(ei); + goto out; + } + + if (ep->dst.s_addr == htonl(EIGRP_MULTICAST_ADDRESS)) + eigrp_if_ipmulticast(eigrp, &ei->address, ei->ifp->ifindex); + + memset(&iph, 0, sizeof(iph)); + memset(&sa_dst, 0, sizeof(sa_dst)); + + /* + * We build and schedule packets to go out + * in the future. In the mean time we may + * process some update packets from the + * neighbor, thus making it necessary + * to update the ack we are using for + * this outgoing packet. + */ + eigrph = (struct eigrp_header *)STREAM_DATA(ep->s); + seqno = ntohl(eigrph->sequence); + ack = ntohl(eigrph->ack); + if (ep->nbr && (ack != ep->nbr->recv_sequence_number)) { + eigrph->ack = htonl(ep->nbr->recv_sequence_number); + ack = ep->nbr->recv_sequence_number; + eigrph->checksum = 0; + eigrp_packet_checksum(ei, ep->s, ep->length); + } + + sa_dst.sin_family = AF_INET; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + sa_dst.sin_len = sizeof(sa_dst); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + sa_dst.sin_addr = ep->dst; + sa_dst.sin_port = htons(0); + + /* Set DONTROUTE flag if dst is unicast. */ + if (!IN_MULTICAST(htonl(ep->dst.s_addr))) + flags = MSG_DONTROUTE; + + iph.ip_hl = sizeof(struct ip) >> EIGRP_WRITE_IPHL_SHIFT; + /* it'd be very strange for header to not be 4byte-word aligned but.. */ + if (sizeof(struct ip) + > (unsigned int)(iph.ip_hl << EIGRP_WRITE_IPHL_SHIFT)) + iph.ip_hl++; /* we presume sizeof struct ip cant overflow + ip_hl.. */ + + iph.ip_v = IPVERSION; + iph.ip_tos = IPTOS_PREC_INTERNETCONTROL; + iph.ip_len = (iph.ip_hl << EIGRP_WRITE_IPHL_SHIFT) + ep->length; + +#if defined(__DragonFly__) + /* + * DragonFly's raw socket expects ip_len/ip_off in network byte order. + */ + iph.ip_len = htons(iph.ip_len); +#endif + + iph.ip_off = 0; + iph.ip_ttl = EIGRP_IP_TTL; + iph.ip_p = IPPROTO_EIGRPIGP; + iph.ip_sum = 0; + iph.ip_src.s_addr = ei->address.u.prefix4.s_addr; + iph.ip_dst.s_addr = ep->dst.s_addr; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (caddr_t)&sa_dst; + msg.msg_namelen = sizeof(sa_dst); + msg.msg_iov = iov; + msg.msg_iovlen = 2; + + iov[0].iov_base = (char *)&iph; + iov[0].iov_len = iph.ip_hl << EIGRP_WRITE_IPHL_SHIFT; + iov[1].iov_base = stream_pnt(ep->s); + iov[1].iov_len = ep->length; + + /* send final fragment (could be first) */ + sockopt_iphdrincl_swab_htosys(&iph); + ret = sendmsg(eigrp->fd, &msg, flags); + sockopt_iphdrincl_swab_systoh(&iph); + + if (IS_DEBUG_EIGRP_TRANSMIT(0, SEND)) { + eigrph = (struct eigrp_header *)STREAM_DATA(ep->s); + zlog_debug( + "Sending [%s][%d/%d] to [%pI4] via [%s] ret [%d].", + lookup_msg(eigrp_packet_type_str, eigrph->opcode, NULL), + seqno, ack, &ep->dst, IF_NAME(ei), ret); + } + + if (ret < 0) + zlog_warn( + "*** sendmsg in eigrp_write failed to %pI4, id %d, off %d, len %d, interface %s, mtu %u: %s", + &iph.ip_dst, iph.ip_id, iph.ip_off, iph.ip_len, + ei->ifp->name, ei->ifp->mtu, safe_strerror(errno)); + + /* Now delete packet from queue. */ + eigrp_packet_delete(ei); + +out: + if (eigrp_fifo_next(ei->obuf) == NULL) { + ei->on_write_q = 0; + list_delete_node(eigrp->oi_write_q, node); + } + + /* If packets still remain in queue, call write thread. */ + if (!list_isempty(eigrp->oi_write_q)) { + event_add_write(master, eigrp_write, eigrp, eigrp->fd, + &eigrp->t_write); + } +} + +/* Starting point of packet process function. */ +void eigrp_read(struct event *thread) +{ + int ret; + struct stream *ibuf; + struct eigrp *eigrp; + struct eigrp_interface *ei; + struct ip *iph; + struct eigrp_header *eigrph; + struct interface *ifp; + struct eigrp_neighbor *nbr; + struct in_addr srcaddr; + uint16_t opcode = 0; + uint16_t length = 0; + + /* first of all get interface pointer. */ + eigrp = EVENT_ARG(thread); + + /* prepare for next packet. */ + event_add_read(master, eigrp_read, eigrp, eigrp->fd, &eigrp->t_read); + + stream_reset(eigrp->ibuf); + if (!(ibuf = eigrp_recv_packet(eigrp, eigrp->fd, &ifp, eigrp->ibuf))) { + /* This raw packet is known to be at least as big as its IP + * header. */ + return; + } + + /* Note that there should not be alignment problems with this assignment + because this is at the beginning of the stream data buffer. */ + iph = (struct ip *)STREAM_DATA(ibuf); + + // Substract IPv4 header size from EIGRP Packet itself + if (iph->ip_v == 4) + length = (iph->ip_len) - 20U; + + srcaddr = iph->ip_src; + + /* IP Header dump. */ + if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV) + && IS_DEBUG_EIGRP_TRANSMIT(0, PACKET_DETAIL)) + eigrp_ip_header_dump(iph); + + /* Note that sockopt_iphdrincl_swab_systoh was called in + * eigrp_recv_packet. */ + if (ifp == NULL) { + struct connected *c; + /* Handle cases where the platform does not support retrieving + the ifindex, + and also platforms (such as Solaris 8) that claim to support + ifindex + retrieval but do not. */ + c = if_lookup_address((void *)&srcaddr, AF_INET, + eigrp->vrf_id); + + if (c == NULL) + return; + + ifp = c->ifp; + } + + /* associate packet with eigrp interface */ + ei = ifp->info; + + /* eigrp_verify_header() relies on a valid "ei" and thus can be called + only + after the checks below are passed. These checks in turn access the + fields of unverified "eigrph" structure for their own purposes and + must remain very accurate in doing this. + */ + if (!ei) + return; + + /* Self-originated packet should be discarded silently. */ + if (eigrp_if_lookup_by_local_addr(eigrp, NULL, iph->ip_src) + || (IPV4_ADDR_SAME(&srcaddr, &ei->address.u.prefix4))) { + if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV)) + zlog_debug( + "eigrp_read[%pI4]: Dropping self-originated packet", + &srcaddr); + return; + } + + /* Advance from IP header to EIGRP header (iph->ip_hl has been verified + by eigrp_recv_packet() to be correct). */ + + stream_forward_getp(ibuf, (iph->ip_hl * 4)); + eigrph = (struct eigrp_header *)stream_pnt(ibuf); + + if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV) + && IS_DEBUG_EIGRP_TRANSMIT(0, PACKET_DETAIL)) + eigrp_header_dump(eigrph); + + if (ntohs(eigrph->ASNumber) != eigrp->AS) { + if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV)) + zlog_debug( + "ignoring packet from router %u sent to %pI4, wrong AS Number received: %u", + ntohs(eigrph->vrid), &iph->ip_dst, + ntohs(eigrph->ASNumber)); + return; + } + + /* If incoming interface is passive one, ignore it. */ + if (eigrp_if_is_passive(ei)) { + if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV)) + zlog_debug( + "ignoring packet from router %u sent to %pI4, received on a passive interface, %pI4", + ntohs(eigrph->vrid), &iph->ip_dst, + &ei->address.u.prefix4); + + if (iph->ip_dst.s_addr == htonl(EIGRP_MULTICAST_ADDRESS)) { + eigrp_if_set_multicast(ei); + } + return; + } + + /* else it must be a local eigrp interface, check it was received on + * correct link + */ + else if (ei->ifp != ifp) { + if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV)) + zlog_warn( + "Packet from [%pI4] received on wrong link %s", + &iph->ip_src, ifp->name); + return; + } + + /* Verify more EIGRP header fields. */ + ret = eigrp_verify_header(ibuf, ei, iph, eigrph); + if (ret < 0) { + if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV)) + zlog_debug( + "eigrp_read[%pI4]: Header check failed, dropping.", + &iph->ip_src); + return; + } + + /* calcualte the eigrp packet length, and move the pounter to the + start of the eigrp TLVs */ + opcode = eigrph->opcode; + + if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV)) + zlog_debug( + "Received [%s][%d/%d] length [%u] via [%s] src [%pI4] dst [%pI4]", + lookup_msg(eigrp_packet_type_str, opcode, NULL), + ntohl(eigrph->sequence), ntohl(eigrph->ack), length, + IF_NAME(ei), &iph->ip_src, &iph->ip_dst); + + /* Read rest of the packet and call each sort of packet routine. */ + stream_forward_getp(ibuf, EIGRP_HEADER_LEN); + + /* New testing block of code for handling Acks */ + if (ntohl(eigrph->ack) != 0) { + struct eigrp_packet *ep = NULL; + + nbr = eigrp_nbr_get(ei, eigrph, iph); + + // neighbor must be valid, eigrp_nbr_get creates if none existed + assert(nbr); + + ep = eigrp_fifo_next(nbr->retrans_queue); + if ((ep) && (ntohl(eigrph->ack) == ep->sequence_number)) { + ep = eigrp_fifo_pop(nbr->retrans_queue); + eigrp_packet_free(ep); + + if ((nbr->state == EIGRP_NEIGHBOR_PENDING) + && (ntohl(eigrph->ack) + == nbr->init_sequence_number)) { + eigrp_nbr_state_set(nbr, EIGRP_NEIGHBOR_UP); + zlog_info( + "Neighbor(%pI4) adjacency became full", + &nbr->src); + nbr->init_sequence_number = 0; + nbr->recv_sequence_number = + ntohl(eigrph->sequence); + eigrp_update_send_EOT(nbr); + } else + eigrp_send_packet_reliably(nbr); + } + ep = eigrp_fifo_next(nbr->multicast_queue); + if (ep) { + if (ntohl(eigrph->ack) == ep->sequence_number) { + ep = eigrp_fifo_pop(nbr->multicast_queue); + eigrp_packet_free(ep); + if (nbr->multicast_queue->count > 0) { + eigrp_send_packet_reliably(nbr); + } + } + } + } + + + switch (opcode) { + case EIGRP_OPC_HELLO: + eigrp_hello_receive(eigrp, iph, eigrph, ibuf, ei, length); + break; + case EIGRP_OPC_PROBE: + // eigrp_probe_receive(eigrp, iph, eigrph, ibuf, ei, + // length); + break; + case EIGRP_OPC_QUERY: + eigrp_query_receive(eigrp, iph, eigrph, ibuf, ei, length); + break; + case EIGRP_OPC_REPLY: + eigrp_reply_receive(eigrp, iph, eigrph, ibuf, ei, length); + break; + case EIGRP_OPC_REQUEST: + // eigrp_request_receive(eigrp, iph, eigrph, ibuf, ei, + // length); + break; + case EIGRP_OPC_SIAQUERY: + eigrp_query_receive(eigrp, iph, eigrph, ibuf, ei, length); + break; + case EIGRP_OPC_SIAREPLY: + eigrp_reply_receive(eigrp, iph, eigrph, ibuf, ei, length); + break; + case EIGRP_OPC_UPDATE: + eigrp_update_receive(eigrp, iph, eigrph, ibuf, ei, length); + break; + default: + zlog_warn( + "interface %s: EIGRP packet header type %d unsupported", + IF_NAME(ei), opcode); + break; + } +} + +static struct stream *eigrp_recv_packet(struct eigrp *eigrp, + int fd, struct interface **ifp, + struct stream *ibuf) +{ + int ret; + struct ip *iph; + uint16_t ip_len; + unsigned int ifindex = 0; + struct iovec iov; + /* Header and data both require alignment. */ + char buff[CMSG_SPACE(SOPT_SIZE_CMSG_IFINDEX_IPV4())]; + struct msghdr msgh; + + memset(&msgh, 0, sizeof(msgh)); + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + msgh.msg_control = (caddr_t)buff; + msgh.msg_controllen = sizeof(buff); + + ret = stream_recvmsg(ibuf, fd, &msgh, 0, (EIGRP_PACKET_MAX_LEN + 1)); + if (ret < 0) { + zlog_warn("stream_recvmsg failed: %s", safe_strerror(errno)); + return NULL; + } + if ((unsigned int)ret < sizeof(*iph)) /* ret must be > 0 now */ + { + zlog_warn( + "%s: discarding runt packet of length %d (ip header size is %u)", + __func__, ret, (unsigned int)sizeof(*iph)); + return NULL; + } + + /* Note that there should not be alignment problems with this assignment + because this is at the beginning of the stream data buffer. */ + iph = (struct ip *)STREAM_DATA(ibuf); + sockopt_iphdrincl_swab_systoh(iph); + + ip_len = iph->ip_len; + +#if defined(__FreeBSD__) && (__FreeBSD_version < 1000000) + /* + * Kernel network code touches incoming IP header parameters, + * before protocol specific processing. + * + * 1) Convert byteorder to host representation. + * --> ip_len, ip_id, ip_off + * + * 2) Adjust ip_len to strip IP header size! + * --> If user process receives entire IP packet via RAW + * socket, it must consider adding IP header size to + * the "ip_len" field of "ip" structure. + * + * For more details, see <netinet/ip_input.c>. + */ + ip_len = ip_len + (iph->ip_hl << 2); +#endif + +#if defined(__DragonFly__) + /* + * in DragonFly's raw socket, ip_len/ip_off are read + * in network byte order. + * As OpenBSD < 200311 adjust ip_len to strip IP header size! + */ + ip_len = ntohs(iph->ip_len) + (iph->ip_hl << 2); +#endif + + ifindex = getsockopt_ifindex(AF_INET, &msgh); + + *ifp = if_lookup_by_index(ifindex, eigrp->vrf_id); + + if (ret != ip_len) { + zlog_warn( + "%s read length mismatch: ip_len is %d, but recvmsg returned %d", + __func__, ip_len, ret); + return NULL; + } + + return ibuf; +} + +struct eigrp_fifo *eigrp_fifo_new(void) +{ + struct eigrp_fifo *new; + + new = XCALLOC(MTYPE_EIGRP_FIFO, sizeof(struct eigrp_fifo)); + return new; +} + +/* Free eigrp packet fifo. */ +void eigrp_fifo_free(struct eigrp_fifo *fifo) +{ + struct eigrp_packet *ep; + struct eigrp_packet *next; + + for (ep = fifo->head; ep; ep = next) { + next = ep->next; + eigrp_packet_free(ep); + } + fifo->head = fifo->tail = NULL; + fifo->count = 0; + + XFREE(MTYPE_EIGRP_FIFO, fifo); +} + +/* Free eigrp fifo entries without destroying fifo itself*/ +void eigrp_fifo_reset(struct eigrp_fifo *fifo) +{ + struct eigrp_packet *ep; + struct eigrp_packet *next; + + for (ep = fifo->head; ep; ep = next) { + next = ep->next; + eigrp_packet_free(ep); + } + fifo->head = fifo->tail = NULL; + fifo->count = 0; +} + +struct eigrp_packet *eigrp_packet_new(size_t size, struct eigrp_neighbor *nbr) +{ + struct eigrp_packet *new; + + new = XCALLOC(MTYPE_EIGRP_PACKET, sizeof(struct eigrp_packet)); + new->s = stream_new(size); + new->retrans_counter = 0; + new->nbr = nbr; + + return new; +} + +void eigrp_send_packet_reliably(struct eigrp_neighbor *nbr) +{ + struct eigrp_packet *ep; + + ep = eigrp_fifo_next(nbr->retrans_queue); + + if (ep) { + struct eigrp_packet *duplicate; + duplicate = eigrp_packet_duplicate(ep, nbr); + /* Add packet to the top of the interface output queue*/ + eigrp_fifo_push(nbr->ei->obuf, duplicate); + + /*Start retransmission timer*/ + event_add_timer(master, eigrp_unack_packet_retrans, nbr, + EIGRP_PACKET_RETRANS_TIME, + &ep->t_retrans_timer); + + /*Increment sequence number counter*/ + nbr->ei->eigrp->sequence_number++; + + /* Hook thread to write packet. */ + if (nbr->ei->on_write_q == 0) { + listnode_add(nbr->ei->eigrp->oi_write_q, nbr->ei); + nbr->ei->on_write_q = 1; + } + event_add_write(master, eigrp_write, nbr->ei->eigrp, + nbr->ei->eigrp->fd, &nbr->ei->eigrp->t_write); + } +} + +/* Calculate EIGRP checksum */ +void eigrp_packet_checksum(struct eigrp_interface *ei, struct stream *s, + uint16_t length) +{ + struct eigrp_header *eigrph; + + eigrph = (struct eigrp_header *)STREAM_DATA(s); + + /* Calculate checksum. */ + eigrph->checksum = in_cksum(eigrph, length); +} + +/* Make EIGRP header. */ +void eigrp_packet_header_init(int type, struct eigrp *eigrp, struct stream *s, + uint32_t flags, uint32_t sequence, uint32_t ack) +{ + struct eigrp_header *eigrph; + + stream_reset(s); + eigrph = (struct eigrp_header *)STREAM_DATA(s); + + eigrph->version = (uint8_t)EIGRP_HEADER_VERSION; + eigrph->opcode = (uint8_t)type; + eigrph->checksum = 0; + + eigrph->vrid = htons(eigrp->vrid); + eigrph->ASNumber = htons(eigrp->AS); + eigrph->ack = htonl(ack); + eigrph->sequence = htonl(sequence); + // if(flags == EIGRP_INIT_FLAG) + // eigrph->sequence = htonl(3); + eigrph->flags = htonl(flags); + + if (IS_DEBUG_EIGRP_TRANSMIT(0, PACKET_DETAIL)) + zlog_debug("Packet Header Init Seq [%u] Ack [%u]", + htonl(eigrph->sequence), htonl(eigrph->ack)); + + stream_forward_endp(s, EIGRP_HEADER_LEN); +} + +/* Add new packet to head of fifo. */ +void eigrp_fifo_push(struct eigrp_fifo *fifo, struct eigrp_packet *ep) +{ + ep->next = fifo->head; + ep->previous = NULL; + + if (fifo->tail == NULL) + fifo->tail = ep; + + if (fifo->count != 0) + fifo->head->previous = ep; + + fifo->head = ep; + + fifo->count++; +} + +/* Return last fifo entry. */ +struct eigrp_packet *eigrp_fifo_next(struct eigrp_fifo *fifo) +{ + return fifo->tail; +} + +void eigrp_packet_delete(struct eigrp_interface *ei) +{ + struct eigrp_packet *ep; + + ep = eigrp_fifo_pop(ei->obuf); + + if (ep) + eigrp_packet_free(ep); +} + +void eigrp_packet_free(struct eigrp_packet *ep) +{ + if (ep->s) + stream_free(ep->s); + + EVENT_OFF(ep->t_retrans_timer); + + XFREE(MTYPE_EIGRP_PACKET, ep); +} + +/* EIGRP Header verification. */ +static int eigrp_verify_header(struct stream *ibuf, struct eigrp_interface *ei, + struct ip *iph, struct eigrp_header *eigrph) +{ + /* Check network mask, Silently discarded. */ + if (!eigrp_check_network_mask(ei, iph->ip_src)) { + zlog_warn( + "interface %s: eigrp_read network address is not same [%pI4]", + IF_NAME(ei), &iph->ip_src); + return -1; + } + // + // /* Check authentication. The function handles logging actions, where + // required. */ + // if (! eigrp_check_auth(ei, eigrph)) + // return -1; + + return 0; +} + +/* Unbound socket will accept any Raw IP packets if proto is matched. + To prevent it, compare src IP address and i/f address with masking + i/f network mask. */ +static int eigrp_check_network_mask(struct eigrp_interface *ei, + struct in_addr ip_src) +{ + struct in_addr mask, me, him; + + if (ei->type == EIGRP_IFTYPE_POINTOPOINT) + return 1; + + masklen2ip(ei->address.prefixlen, &mask); + + me.s_addr = ei->address.u.prefix4.s_addr & mask.s_addr; + him.s_addr = ip_src.s_addr & mask.s_addr; + + if (IPV4_ADDR_SAME(&me, &him)) + return 1; + + return 0; +} + +void eigrp_unack_packet_retrans(struct event *thread) +{ + struct eigrp_neighbor *nbr; + nbr = (struct eigrp_neighbor *)EVENT_ARG(thread); + + struct eigrp_packet *ep; + ep = eigrp_fifo_next(nbr->retrans_queue); + + if (ep) { + struct eigrp_packet *duplicate; + duplicate = eigrp_packet_duplicate(ep, nbr); + + /* Add packet to the top of the interface output queue*/ + eigrp_fifo_push(nbr->ei->obuf, duplicate); + + ep->retrans_counter++; + if (ep->retrans_counter == EIGRP_PACKET_RETRANS_MAX) { + eigrp_retrans_count_exceeded(ep, nbr); + return; + } + + /*Start retransmission timer*/ + event_add_timer(master, eigrp_unack_packet_retrans, nbr, + EIGRP_PACKET_RETRANS_TIME, + &ep->t_retrans_timer); + + /* Hook thread to write packet. */ + if (nbr->ei->on_write_q == 0) { + listnode_add(nbr->ei->eigrp->oi_write_q, nbr->ei); + nbr->ei->on_write_q = 1; + } + event_add_write(master, eigrp_write, nbr->ei->eigrp, + nbr->ei->eigrp->fd, &nbr->ei->eigrp->t_write); + } +} + +void eigrp_unack_multicast_packet_retrans(struct event *thread) +{ + struct eigrp_neighbor *nbr; + nbr = (struct eigrp_neighbor *)EVENT_ARG(thread); + + struct eigrp_packet *ep; + ep = eigrp_fifo_next(nbr->multicast_queue); + + if (ep) { + struct eigrp_packet *duplicate; + duplicate = eigrp_packet_duplicate(ep, nbr); + /* Add packet to the top of the interface output queue*/ + eigrp_fifo_push(nbr->ei->obuf, duplicate); + + ep->retrans_counter++; + if (ep->retrans_counter == EIGRP_PACKET_RETRANS_MAX) { + eigrp_retrans_count_exceeded(ep, nbr); + return; + } + + /*Start retransmission timer*/ + event_add_timer(master, eigrp_unack_multicast_packet_retrans, + nbr, EIGRP_PACKET_RETRANS_TIME, + &ep->t_retrans_timer); + + /* Hook thread to write packet. */ + if (nbr->ei->on_write_q == 0) { + listnode_add(nbr->ei->eigrp->oi_write_q, nbr->ei); + nbr->ei->on_write_q = 1; + } + event_add_write(master, eigrp_write, nbr->ei->eigrp, + nbr->ei->eigrp->fd, &nbr->ei->eigrp->t_write); + } +} + +/* Get packet from tail of fifo. */ +struct eigrp_packet *eigrp_fifo_pop(struct eigrp_fifo *fifo) +{ + struct eigrp_packet *ep = NULL; + + ep = fifo->tail; + + if (ep) { + fifo->tail = ep->previous; + + if (fifo->tail == NULL) + fifo->head = NULL; + else + fifo->tail->next = NULL; + + fifo->count--; + } + + return ep; +} + +struct eigrp_packet *eigrp_packet_duplicate(struct eigrp_packet *old, + struct eigrp_neighbor *nbr) +{ + struct eigrp_packet *new; + + new = eigrp_packet_new(EIGRP_PACKET_MTU(nbr->ei->ifp->mtu), nbr); + new->length = old->length; + new->retrans_counter = old->retrans_counter; + new->dst = old->dst; + new->sequence_number = old->sequence_number; + stream_copy(new->s, old->s); + + return new; +} + +static struct TLV_IPv4_Internal_type *eigrp_IPv4_InternalTLV_new(void) +{ + struct TLV_IPv4_Internal_type *new; + + new = XCALLOC(MTYPE_EIGRP_IPV4_INT_TLV, + sizeof(struct TLV_IPv4_Internal_type)); + + return new; +} + +struct TLV_IPv4_Internal_type *eigrp_read_ipv4_tlv(struct stream *s) +{ + struct TLV_IPv4_Internal_type *tlv; + uint32_t destination_tmp; + + tlv = eigrp_IPv4_InternalTLV_new(); + + tlv->type = stream_getw(s); + tlv->length = stream_getw(s); + tlv->forward.s_addr = stream_getl(s); + tlv->metric.delay = stream_getl(s); + tlv->metric.bandwidth = stream_getl(s); + tlv->metric.mtu[0] = stream_getc(s); + tlv->metric.mtu[1] = stream_getc(s); + tlv->metric.mtu[2] = stream_getc(s); + tlv->metric.hop_count = stream_getc(s); + tlv->metric.reliability = stream_getc(s); + tlv->metric.load = stream_getc(s); + tlv->metric.tag = stream_getc(s); + tlv->metric.flags = stream_getc(s); + + tlv->prefix_length = stream_getc(s); + + destination_tmp = stream_getc(s) << 24; + if (tlv->prefix_length > 8) + destination_tmp |= stream_getc(s) << 16; + if (tlv->prefix_length > 16) + destination_tmp |= stream_getc(s) << 8; + if (tlv->prefix_length > 24) + destination_tmp |= stream_getc(s); + + tlv->destination.s_addr = htonl(destination_tmp); + + return tlv; +} + +uint16_t eigrp_add_internalTLV_to_stream(struct stream *s, + struct eigrp_prefix_descriptor *pe) +{ + uint16_t length; + + stream_putw(s, EIGRP_TLV_IPv4_INT); + switch (pe->destination->prefixlen) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + length = EIGRP_TLV_IPV4_SIZE_GRT_0_BIT; + stream_putw(s, length); + break; + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + length = EIGRP_TLV_IPV4_SIZE_GRT_8_BIT; + stream_putw(s, length); + break; + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + length = EIGRP_TLV_IPV4_SIZE_GRT_16_BIT; + stream_putw(s, length); + break; + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + case 31: + case 32: + length = EIGRP_TLV_IPV4_SIZE_GRT_24_BIT; + stream_putw(s, length); + break; + default: + flog_err(EC_LIB_DEVELOPMENT, "%s: Unexpected prefix length: %d", + __func__, pe->destination->prefixlen); + return 0; + } + stream_putl(s, 0x00000000); + + /*Metric*/ + stream_putl(s, pe->reported_metric.delay); + stream_putl(s, pe->reported_metric.bandwidth); + stream_putc(s, pe->reported_metric.mtu[2]); + stream_putc(s, pe->reported_metric.mtu[1]); + stream_putc(s, pe->reported_metric.mtu[0]); + stream_putc(s, pe->reported_metric.hop_count); + stream_putc(s, pe->reported_metric.reliability); + stream_putc(s, pe->reported_metric.load); + stream_putc(s, pe->reported_metric.tag); + stream_putc(s, pe->reported_metric.flags); + + stream_putc(s, pe->destination->prefixlen); + + stream_putc(s, (ntohl(pe->destination->u.prefix4.s_addr) >> 24) & 0xFF); + if (pe->destination->prefixlen > 8) + stream_putc(s, (ntohl(pe->destination->u.prefix4.s_addr) >> 16) & 0xFF); + if (pe->destination->prefixlen > 16) + stream_putc(s, (ntohl(pe->destination->u.prefix4.s_addr) >> 8) & 0xFF); + if (pe->destination->prefixlen > 24) + stream_putc(s, ntohl(pe->destination->u.prefix4.s_addr) & 0xFF); + + return length; +} + +uint16_t eigrp_add_authTLV_MD5_to_stream(struct stream *s, + struct eigrp_interface *ei) +{ + struct key *key; + struct keychain *keychain; + struct TLV_MD5_Authentication_Type *authTLV; + + authTLV = eigrp_authTLV_MD5_new(); + + authTLV->type = htons(EIGRP_TLV_AUTH); + authTLV->length = htons(EIGRP_AUTH_MD5_TLV_SIZE); + authTLV->auth_type = htons(EIGRP_AUTH_TYPE_MD5); + authTLV->auth_length = htons(EIGRP_AUTH_TYPE_MD5_LEN); + authTLV->key_sequence = 0; + memset(authTLV->Nullpad, 0, sizeof(authTLV->Nullpad)); + + keychain = keychain_lookup(ei->params.auth_keychain); + if (keychain) + key = key_lookup_for_send(keychain); + else { + free(ei->params.auth_keychain); + ei->params.auth_keychain = NULL; + eigrp_authTLV_MD5_free(authTLV); + return 0; + } + + if (key) { + authTLV->key_id = htonl(key->index); + memset(authTLV->digest, 0, EIGRP_AUTH_TYPE_MD5_LEN); + stream_put(s, authTLV, + sizeof(struct TLV_MD5_Authentication_Type)); + eigrp_authTLV_MD5_free(authTLV); + return EIGRP_AUTH_MD5_TLV_SIZE; + } + + eigrp_authTLV_MD5_free(authTLV); + + return 0; +} + +uint16_t eigrp_add_authTLV_SHA256_to_stream(struct stream *s, + struct eigrp_interface *ei) +{ + struct key *key; + struct keychain *keychain; + struct TLV_SHA256_Authentication_Type *authTLV; + + authTLV = eigrp_authTLV_SHA256_new(); + + authTLV->type = htons(EIGRP_TLV_AUTH); + authTLV->length = htons(EIGRP_AUTH_SHA256_TLV_SIZE); + authTLV->auth_type = htons(EIGRP_AUTH_TYPE_SHA256); + authTLV->auth_length = htons(EIGRP_AUTH_TYPE_SHA256_LEN); + authTLV->key_sequence = 0; + memset(authTLV->Nullpad, 0, sizeof(authTLV->Nullpad)); + + keychain = keychain_lookup(ei->params.auth_keychain); + if (keychain) + key = key_lookup_for_send(keychain); + else { + free(ei->params.auth_keychain); + ei->params.auth_keychain = NULL; + eigrp_authTLV_SHA256_free(authTLV); + return 0; + } + + if (key) { + authTLV->key_id = 0; + memset(authTLV->digest, 0, EIGRP_AUTH_TYPE_SHA256_LEN); + stream_put(s, authTLV, + sizeof(struct TLV_SHA256_Authentication_Type)); + eigrp_authTLV_SHA256_free(authTLV); + return EIGRP_AUTH_SHA256_TLV_SIZE; + } + + eigrp_authTLV_SHA256_free(authTLV); + + return 0; +} + +struct TLV_MD5_Authentication_Type *eigrp_authTLV_MD5_new(void) +{ + struct TLV_MD5_Authentication_Type *new; + + new = XCALLOC(MTYPE_EIGRP_AUTH_TLV, + sizeof(struct TLV_MD5_Authentication_Type)); + + return new; +} + +void eigrp_authTLV_MD5_free(struct TLV_MD5_Authentication_Type *authTLV) +{ + XFREE(MTYPE_EIGRP_AUTH_TLV, authTLV); +} + +struct TLV_SHA256_Authentication_Type *eigrp_authTLV_SHA256_new(void) +{ + struct TLV_SHA256_Authentication_Type *new; + + new = XCALLOC(MTYPE_EIGRP_AUTH_SHA256_TLV, + sizeof(struct TLV_SHA256_Authentication_Type)); + + return new; +} + +void eigrp_authTLV_SHA256_free(struct TLV_SHA256_Authentication_Type *authTLV) +{ + XFREE(MTYPE_EIGRP_AUTH_SHA256_TLV, authTLV); +} + +void eigrp_IPv4_InternalTLV_free( + struct TLV_IPv4_Internal_type *IPv4_InternalTLV) +{ + XFREE(MTYPE_EIGRP_IPV4_INT_TLV, IPv4_InternalTLV); +} + +struct TLV_Sequence_Type *eigrp_SequenceTLV_new(void) +{ + struct TLV_Sequence_Type *new; + + new = XCALLOC(MTYPE_EIGRP_SEQ_TLV, sizeof(struct TLV_Sequence_Type)); + + return new; +} diff --git a/eigrpd/eigrp_packet.h b/eigrpd/eigrp_packet.h new file mode 100644 index 0000000..6a0360e --- /dev/null +++ b/eigrpd/eigrp_packet.h @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP General Sending and Receiving of EIGRP Packets. + * Copyright (C) 2013-2016 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * Frantisek Gazo + * Tomas Hvorkovy + * Martin Kontsek + * Lukas Koribsky + */ + +#ifndef _ZEBRA_EIGRP_PACKET_H +#define _ZEBRA_EIGRP_PACKET_H + +/*Prototypes*/ +extern void eigrp_read(struct event *thread); +extern void eigrp_write(struct event *thread); + +extern struct eigrp_packet *eigrp_packet_new(size_t size, + struct eigrp_neighbor *nbr); +extern struct eigrp_packet *eigrp_packet_duplicate(struct eigrp_packet *old, + struct eigrp_neighbor *nbr); +extern void eigrp_packet_free(struct eigrp_packet *ep); +extern void eigrp_packet_delete(struct eigrp_interface *ei); +extern void eigrp_packet_header_init(int type, struct eigrp *eigrp, + struct stream *s, uint32_t flags, + uint32_t sequence, uint32_t ack); +extern void eigrp_packet_checksum(struct eigrp_interface *ei, struct stream *s, + uint16_t length); + +extern struct eigrp_fifo *eigrp_fifo_new(void); +extern struct eigrp_packet *eigrp_fifo_next(struct eigrp_fifo *fifo); +extern struct eigrp_packet *eigrp_fifo_pop(struct eigrp_fifo *fifo); +extern void eigrp_fifo_push(struct eigrp_fifo *fifo, struct eigrp_packet *ep); +extern void eigrp_fifo_free(struct eigrp_fifo *fifo); +extern void eigrp_fifo_reset(struct eigrp_fifo *fifo); + +extern void eigrp_send_packet_reliably(struct eigrp_neighbor *nbr); + +extern struct TLV_IPv4_Internal_type *eigrp_read_ipv4_tlv(struct stream *s); +extern uint16_t +eigrp_add_internalTLV_to_stream(struct stream *s, + struct eigrp_prefix_descriptor *pe); +extern uint16_t eigrp_add_authTLV_MD5_to_stream(struct stream *s, + struct eigrp_interface *ei); +extern uint16_t eigrp_add_authTLV_SHA256_to_stream(struct stream *s, + struct eigrp_interface *ei); + +extern void eigrp_unack_packet_retrans(struct event *thread); +extern void eigrp_unack_multicast_packet_retrans(struct event *thread); + +/* + * untill there is reason to have their own header, these externs are found in + * eigrp_hello.c + */ +extern void eigrp_sw_version_initialize(void); +extern void eigrp_hello_send(struct eigrp_interface *ei, uint8_t flags, + struct in_addr *nbr_addr); +extern void eigrp_hello_send_ack(struct eigrp_neighbor *nbr); +extern void eigrp_hello_receive(struct eigrp *eigrp, struct ip *iph, + struct eigrp_header *eigrph, struct stream *s, + struct eigrp_interface *ei, int size); +extern void eigrp_hello_timer(struct event *thread); + +/* + * These externs are found in eigrp_update.c + */ +extern bool eigrp_update_prefix_apply(struct eigrp *eigrp, + struct eigrp_interface *ei, int in, + struct prefix *prefix); +extern void eigrp_update_send(struct eigrp_interface *ei); +extern void eigrp_update_receive(struct eigrp *eigrp, struct ip *iph, + struct eigrp_header *eigrph, struct stream *s, + struct eigrp_interface *ei, int size); +extern void eigrp_update_send_all(struct eigrp *eigrp, + struct eigrp_interface *exception); +extern void eigrp_update_send_init(struct eigrp_neighbor *nbr); +extern void eigrp_update_send_EOT(struct eigrp_neighbor *nbr); +extern void eigrp_update_send_GR_thread(struct event *thread); +extern void eigrp_update_send_GR(struct eigrp_neighbor *nbr, + enum GR_type gr_type, struct vty *vty); +extern void eigrp_update_send_interface_GR(struct eigrp_interface *ei, + enum GR_type gr_type, + struct vty *vty); +extern void eigrp_update_send_process_GR(struct eigrp *eigrp, + enum GR_type gr_type, struct vty *vty); + +/* + * These externs are found in eigrp_query.c + */ + +extern void eigrp_send_query(struct eigrp_interface *ei); +extern void eigrp_query_receive(struct eigrp *eigrp, struct ip *iph, + struct eigrp_header *eigrph, struct stream *s, + struct eigrp_interface *ei, int size); +extern uint32_t eigrp_query_send_all(struct eigrp *eigrp); + +/* + * These externs are found in eigrp_reply.c + */ +extern void eigrp_send_reply(struct eigrp_neighbor *nbr, + struct eigrp_prefix_descriptor *pe); +extern void eigrp_reply_receive(struct eigrp *eigrp, struct ip *iph, + struct eigrp_header *eigrph, struct stream *s, + struct eigrp_interface *ei, int size); + +/* + * These externs are found in eigrp_siaquery.c + */ +extern void eigrp_send_siaquery(struct eigrp_neighbor *nbr, + struct eigrp_prefix_descriptor *pe); +extern void eigrp_siaquery_receive(struct eigrp *eigrp, struct ip *iph, + struct eigrp_header *eigrph, + struct stream *s, struct eigrp_interface *ei, + int size); + +/* + * These externs are found in eigrp_siareply.c + */ +extern void eigrp_send_siareply(struct eigrp_neighbor *nbr, + struct eigrp_prefix_descriptor *pe); +extern void eigrp_siareply_receive(struct eigrp *eigrp, struct ip *iph, + struct eigrp_header *eigrph, + struct stream *s, struct eigrp_interface *ei, + int size); + +extern struct TLV_MD5_Authentication_Type *eigrp_authTLV_MD5_new(void); +extern void eigrp_authTLV_MD5_free(struct TLV_MD5_Authentication_Type *authTLV); +extern struct TLV_SHA256_Authentication_Type *eigrp_authTLV_SHA256_new(void); +extern void +eigrp_authTLV_SHA256_free(struct TLV_SHA256_Authentication_Type *authTLV); + +extern int eigrp_make_md5_digest(struct eigrp_interface *ei, struct stream *s, + uint8_t flags); +extern int eigrp_check_md5_digest(struct stream *s, + struct TLV_MD5_Authentication_Type *authTLV, + struct eigrp_neighbor *nbr, uint8_t flags); +extern int eigrp_make_sha256_digest(struct eigrp_interface *ei, + struct stream *s, uint8_t flags); +extern int +eigrp_check_sha256_digest(struct stream *s, + struct TLV_SHA256_Authentication_Type *authTLV, + struct eigrp_neighbor *nbr, uint8_t flags); + + +extern void +eigrp_IPv4_InternalTLV_free(struct TLV_IPv4_Internal_type *IPv4_InternalTLV); + +extern struct TLV_Sequence_Type *eigrp_SequenceTLV_new(void); + +extern const struct message eigrp_packet_type_str[]; +extern const size_t eigrp_packet_type_str_max; + +#endif /* _ZEBRA_EIGRP_PACKET_H */ diff --git a/eigrpd/eigrp_pkt_tlv1.c b/eigrpd/eigrp_pkt_tlv1.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/eigrpd/eigrp_pkt_tlv1.c diff --git a/eigrpd/eigrp_pkt_tlv2.c b/eigrpd/eigrp_pkt_tlv2.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/eigrpd/eigrp_pkt_tlv2.c diff --git a/eigrpd/eigrp_query.c b/eigrpd/eigrp_query.c new file mode 100644 index 0000000..0e206cd --- /dev/null +++ b/eigrpd/eigrp_query.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Sending and Receiving EIGRP Query Packets. + * Copyright (C) 2013-2014 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + */ + +#include <zebra.h> + +#include "frrevent.h" +#include "memory.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "sockunion.h" +#include "stream.h" +#include "log.h" +#include "sockopt.h" +#include "checksum.h" +#include "md5.h" +#include "vty.h" + +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_interface.h" +#include "eigrpd/eigrp_neighbor.h" +#include "eigrpd/eigrp_packet.h" +#include "eigrpd/eigrp_zebra.h" +#include "eigrpd/eigrp_vty.h" +#include "eigrpd/eigrp_dump.h" +#include "eigrpd/eigrp_macros.h" +#include "eigrpd/eigrp_topology.h" +#include "eigrpd/eigrp_fsm.h" + +uint32_t eigrp_query_send_all(struct eigrp *eigrp) +{ + struct eigrp_interface *iface; + struct listnode *node, *node2, *nnode2; + struct eigrp_prefix_descriptor *pe; + uint32_t counter; + + if (eigrp == NULL) { + zlog_debug("EIGRP Routing Process not enabled"); + return 0; + } + + counter = 0; + for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, iface)) { + eigrp_send_query(iface); + counter++; + } + + for (ALL_LIST_ELEMENTS(eigrp->topology_changes_internalIPV4, node2, + nnode2, pe)) { + if (pe->req_action & EIGRP_FSM_NEED_QUERY) { + pe->req_action &= ~EIGRP_FSM_NEED_QUERY; + listnode_delete(eigrp->topology_changes_internalIPV4, + pe); + } + } + + return counter; +} + +/*EIGRP QUERY read function*/ +void eigrp_query_receive(struct eigrp *eigrp, struct ip *iph, + struct eigrp_header *eigrph, struct stream *s, + struct eigrp_interface *ei, int size) +{ + struct eigrp_neighbor *nbr; + struct TLV_IPv4_Internal_type *tlv; + struct prefix dest_addr; + + uint16_t type; + uint16_t length; + + /* increment statistics. */ + ei->query_in++; + + /* get neighbor struct */ + nbr = eigrp_nbr_get(ei, eigrph, iph); + + /* neighbor must be valid, eigrp_nbr_get creates if none existed */ + assert(nbr); + + nbr->recv_sequence_number = ntohl(eigrph->sequence); + + while (s->endp > s->getp) { + type = stream_getw(s); + switch (type) { + case EIGRP_TLV_IPv4_INT: + stream_set_getp(s, s->getp - sizeof(uint16_t)); + + tlv = eigrp_read_ipv4_tlv(s); + + dest_addr.family = AF_INET; + dest_addr.u.prefix4 = tlv->destination; + dest_addr.prefixlen = tlv->prefix_length; + struct eigrp_prefix_descriptor *dest = + eigrp_topology_table_lookup_ipv4( + eigrp->topology_table, &dest_addr); + + /* If the destination exists (it should, but one never + * know)*/ + if (dest != NULL) { + struct eigrp_fsm_action_message msg; + struct eigrp_route_descriptor *entry = + eigrp_route_descriptor_lookup( + dest->entries, nbr); + msg.packet_type = EIGRP_OPC_QUERY; + msg.eigrp = eigrp; + msg.data_type = EIGRP_INT; + msg.adv_router = nbr; + msg.metrics = tlv->metric; + msg.entry = entry; + msg.prefix = dest; + eigrp_fsm_event(&msg); + } + eigrp_IPv4_InternalTLV_free(tlv); + break; + + case EIGRP_TLV_IPv4_EXT: + /* DVS: processing of external routes needs packet and fsm work. + * for now, lets just not creash the box + */ + default: + length = stream_getw(s); + // -2 for type, -2 for len + for (length -= 4; length; length--) { + (void)stream_getc(s); + } + } + } + eigrp_hello_send_ack(nbr); + eigrp_query_send_all(eigrp); + eigrp_update_send_all(eigrp, nbr->ei); +} + +void eigrp_send_query(struct eigrp_interface *ei) +{ + struct eigrp_packet *ep = NULL; + uint16_t length = EIGRP_HEADER_LEN; + struct listnode *node, *nnode, *node2, *nnode2; + struct eigrp_neighbor *nbr; + struct eigrp_prefix_descriptor *pe; + bool has_tlv = false; + bool new_packet = true; + uint16_t eigrp_mtu = EIGRP_PACKET_MTU(ei->ifp->mtu); + + for (ALL_LIST_ELEMENTS(ei->eigrp->topology_changes_internalIPV4, node, + nnode, pe)) { + if (!(pe->req_action & EIGRP_FSM_NEED_QUERY)) + continue; + + if (new_packet) { + ep = eigrp_packet_new(eigrp_mtu, NULL); + + /* Prepare EIGRP INIT UPDATE header */ + eigrp_packet_header_init(EIGRP_OPC_QUERY, ei->eigrp, + ep->s, 0, + ei->eigrp->sequence_number, 0); + + // encode Authentication TLV, if needed + if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) + && (ei->params.auth_keychain != NULL)) { + length += eigrp_add_authTLV_MD5_to_stream(ep->s, + ei); + } + new_packet = false; + } + + length += eigrp_add_internalTLV_to_stream(ep->s, pe); + has_tlv = true; + for (ALL_LIST_ELEMENTS(ei->nbrs, node2, nnode2, nbr)) { + if (nbr->state == EIGRP_NEIGHBOR_UP) + listnode_add(pe->rij, nbr); + } + + if (length + EIGRP_TLV_MAX_IPV4_BYTE > eigrp_mtu) { + if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) + && ei->params.auth_keychain != NULL) { + eigrp_make_md5_digest(ei, ep->s, + EIGRP_AUTH_UPDATE_FLAG); + } + + eigrp_packet_checksum(ei, ep->s, length); + ep->length = length; + + ep->dst.s_addr = htonl(EIGRP_MULTICAST_ADDRESS); + + ep->sequence_number = ei->eigrp->sequence_number; + ei->eigrp->sequence_number++; + + for (ALL_LIST_ELEMENTS(ei->nbrs, node2, nnode2, nbr)) { + struct eigrp_packet *dup; + + if (nbr->state != EIGRP_NEIGHBOR_UP) + continue; + + dup = eigrp_packet_duplicate(ep, nbr); + /*Put packet to retransmission queue*/ + eigrp_fifo_push(nbr->retrans_queue, dup); + + if (nbr->retrans_queue->count == 1) + eigrp_send_packet_reliably(nbr); + } + + has_tlv = false; + length = 0; + eigrp_packet_free(ep); + ep = NULL; + new_packet = true; + } + } + + if (!has_tlv) + return; + + if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) + && ei->params.auth_keychain != NULL) + eigrp_make_md5_digest(ei, ep->s, EIGRP_AUTH_UPDATE_FLAG); + + + /* EIGRP Checksum */ + eigrp_packet_checksum(ei, ep->s, length); + + ep->length = length; + ep->dst.s_addr = htonl(EIGRP_MULTICAST_ADDRESS); + + /*This ack number we await from neighbor*/ + ep->sequence_number = ei->eigrp->sequence_number; + ei->eigrp->sequence_number++; + + for (ALL_LIST_ELEMENTS(ei->nbrs, node2, nnode2, nbr)) { + struct eigrp_packet *dup; + + if (nbr->state != EIGRP_NEIGHBOR_UP) + continue; + + dup = eigrp_packet_duplicate(ep, nbr); + /*Put packet to retransmission queue*/ + eigrp_fifo_push(nbr->retrans_queue, dup); + + if (nbr->retrans_queue->count == 1) + eigrp_send_packet_reliably(nbr); + } + + eigrp_packet_free(ep); +} diff --git a/eigrpd/eigrp_reply.c b/eigrpd/eigrp_reply.c new file mode 100644 index 0000000..aae89e8 --- /dev/null +++ b/eigrpd/eigrp_reply.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Eigrp Sending and Receiving EIGRP Reply Packets. + * Copyright (C) 2013-2016 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * Frantisek Gazo + * Tomas Hvorkovy + * Martin Kontsek + * Lukas Koribsky + */ + +#include <zebra.h> + +#include "frrevent.h" +#include "memory.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "sockunion.h" +#include "stream.h" +#include "log.h" +#include "sockopt.h" +#include "checksum.h" +#include "md5.h" +#include "vty.h" +#include "keychain.h" +#include "plist.h" + +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_interface.h" +#include "eigrpd/eigrp_neighbor.h" +#include "eigrpd/eigrp_packet.h" +#include "eigrpd/eigrp_zebra.h" +#include "eigrpd/eigrp_vty.h" +#include "eigrpd/eigrp_dump.h" +#include "eigrpd/eigrp_macros.h" +#include "eigrpd/eigrp_topology.h" +#include "eigrpd/eigrp_fsm.h" +#include "eigrpd/eigrp_errors.h" + +void eigrp_send_reply(struct eigrp_neighbor *nbr, + struct eigrp_prefix_descriptor *pe) +{ + struct eigrp_packet *ep; + uint16_t length = EIGRP_HEADER_LEN; + struct eigrp_interface *ei = nbr->ei; + struct eigrp *eigrp = ei->eigrp; + struct eigrp_prefix_descriptor *pe2; + + // TODO: Work in progress + /* Filtering */ + /* get list from eigrp process */ + pe2 = XCALLOC(MTYPE_EIGRP_PREFIX_DESCRIPTOR, + sizeof(struct eigrp_prefix_descriptor)); + memcpy(pe2, pe, sizeof(struct eigrp_prefix_descriptor)); + + if (eigrp_update_prefix_apply(eigrp, ei, EIGRP_FILTER_OUT, + pe2->destination)) { + zlog_info("REPLY SEND: Setting Metric to max"); + pe2->reported_metric.delay = EIGRP_MAX_METRIC; + } + + /* + * End of filtering + */ + + ep = eigrp_packet_new(EIGRP_PACKET_MTU(ei->ifp->mtu), nbr); + + /* Prepare EIGRP INIT UPDATE header */ + eigrp_packet_header_init(EIGRP_OPC_REPLY, eigrp, ep->s, 0, + eigrp->sequence_number, 0); + + // encode Authentication TLV, if needed + if (ei->params.auth_type == EIGRP_AUTH_TYPE_MD5 + && (ei->params.auth_keychain != NULL)) { + length += eigrp_add_authTLV_MD5_to_stream(ep->s, ei); + } + + + length += eigrp_add_internalTLV_to_stream(ep->s, pe2); + + if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) + && (ei->params.auth_keychain != NULL)) { + eigrp_make_md5_digest(ei, ep->s, EIGRP_AUTH_UPDATE_FLAG); + } + + /* EIGRP Checksum */ + eigrp_packet_checksum(ei, ep->s, length); + + ep->length = length; + ep->dst.s_addr = nbr->src.s_addr; + + /*This ack number we await from neighbor*/ + ep->sequence_number = eigrp->sequence_number; + + /*Put packet to retransmission queue*/ + eigrp_fifo_push(nbr->retrans_queue, ep); + + if (nbr->retrans_queue->count == 1) { + eigrp_send_packet_reliably(nbr); + } + + XFREE(MTYPE_EIGRP_PREFIX_DESCRIPTOR, pe2); +} + +/*EIGRP REPLY read function*/ +void eigrp_reply_receive(struct eigrp *eigrp, struct ip *iph, + struct eigrp_header *eigrph, struct stream *s, + struct eigrp_interface *ei, int size) +{ + struct eigrp_neighbor *nbr; + struct TLV_IPv4_Internal_type *tlv; + + uint16_t type; + + /* increment statistics. */ + ei->reply_in++; + + /* get neighbor struct */ + nbr = eigrp_nbr_get(ei, eigrph, iph); + + /* neighbor must be valid, eigrp_nbr_get creates if none existed */ + assert(nbr); + + nbr->recv_sequence_number = ntohl(eigrph->sequence); + + while (s->endp > s->getp) { + type = stream_getw(s); + + if (type != EIGRP_TLV_IPv4_INT) + continue; + + struct prefix dest_addr; + + stream_set_getp(s, s->getp - sizeof(uint16_t)); + + tlv = eigrp_read_ipv4_tlv(s); + + dest_addr.family = AF_INET; + dest_addr.u.prefix4 = tlv->destination; + dest_addr.prefixlen = tlv->prefix_length; + struct eigrp_prefix_descriptor *dest = + eigrp_topology_table_lookup_ipv4(eigrp->topology_table, + &dest_addr); + /* + * Destination must exists + */ + if (!dest) { + flog_err( + EC_EIGRP_PACKET, + "%s: Received prefix %pFX which we do not know about", + __func__, &dest_addr); + eigrp_IPv4_InternalTLV_free(tlv); + continue; + } + + struct eigrp_fsm_action_message msg; + struct eigrp_route_descriptor *entry = + eigrp_route_descriptor_lookup(dest->entries, nbr); + + if (eigrp_update_prefix_apply(eigrp, ei, EIGRP_FILTER_IN, + &dest_addr)) { + tlv->metric.delay = EIGRP_MAX_METRIC; + } + /* + * End of filtering + */ + + msg.packet_type = EIGRP_OPC_REPLY; + msg.eigrp = eigrp; + msg.data_type = EIGRP_INT; + msg.adv_router = nbr; + msg.metrics = tlv->metric; + msg.entry = entry; + msg.prefix = dest; + eigrp_fsm_event(&msg); + + eigrp_IPv4_InternalTLV_free(tlv); + } + eigrp_hello_send_ack(nbr); +} diff --git a/eigrpd/eigrp_routemap.c b/eigrpd/eigrp_routemap.c new file mode 100644 index 0000000..84f27d0 --- /dev/null +++ b/eigrpd/eigrp_routemap.c @@ -0,0 +1,1210 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Filter Functions. + * Copyright (C) 2013-2015 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * Frantisek Gazo + * Tomas Hvorkovy + * Martin Kontsek + * Lukas Koribsky + * + * Note: This file contains skeleton for all possible matches and sets, + * but they are hidden in comment block and not properly implemented. + * At this time, the only function we consider useful for our use + * in distribute command in EIGRP is matching destination IP (with both + * access and prefix list). + * + */ + +#include <zebra.h> + +#include "memory.h" +#include "prefix.h" +#include "if_rmap.h" +#include "routemap.h" +#include "command.h" +#include "filter.h" +#include "log.h" +#include "sockunion.h" /* for inet_aton () */ +#include "plist.h" + +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrp_const.h" +#include "eigrpd/eigrp_macros.h" +#include "eigrpd/eigrp_routemap.h" + +void eigrp_if_rmap_update(struct if_rmap *if_rmap) +{ + struct interface *ifp; + struct eigrp_interface *ei, *ei2; + struct listnode *node, *nnode; + struct route_map *rmap; + struct eigrp *e; + + ifp = if_lookup_by_name(if_rmap->ifname); + if (ifp == NULL) + return; + + ei = NULL; + e = eigrp_lookup(); + for (ALL_LIST_ELEMENTS(e->eiflist, node, nnode, ei2)) { + if (strcmp(ei2->ifp->name, ifp->name) == 0) { + ei = ei2; + break; + } + } + + if (if_rmap->routemap[IF_RMAP_IN]) { + rmap = route_map_lookup_by_name(if_rmap->routemap[IF_RMAP_IN]); + if (rmap) + ei->routemap[IF_RMAP_IN] = rmap; + else + ei->routemap[IF_RMAP_IN] = NULL; + } else + ei->routemap[EIGRP_FILTER_IN] = NULL; + + if (if_rmap->routemap[IF_RMAP_OUT]) { + rmap = route_map_lookup_by_name(if_rmap->routemap[IF_RMAP_OUT]); + if (rmap) + ei->routemap[IF_RMAP_OUT] = rmap; + else + ei->routemap[IF_RMAP_OUT] = NULL; + } else + ei->routemap[EIGRP_FILTER_OUT] = NULL; +} + +void eigrp_if_rmap_update_interface(struct interface *ifp) +{ + struct if_rmap *if_rmap; + + if_rmap = if_rmap_lookup(ifp->name); + if (if_rmap) + eigrp_if_rmap_update(if_rmap); +} + +void eigrp_routemap_update_redistribute(void) +{ + int i; + struct eigrp *e; + + e = eigrp_lookup(); + + if (e) { + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { + if (e->route_map[i].name) + e->route_map[i].map = route_map_lookup_by_name( + e->route_map[i].name); + } + } +} + +/* ARGSUSED */ +void eigrp_rmap_update(const char *notused) +{ + struct interface *ifp; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(iflist, node, nnode, ifp)) + eigrp_if_rmap_update_interface(ifp); + + eigrp_routemap_update_redistribute(); +} + +/* Add eigrp route map rule. */ +static int eigrp_route_match_add(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg) +{ + enum rmap_compile_rets ret; + + ret = route_map_add_match(index, command, arg, type); + switch (ret) { + case RMAP_RULE_MISSING: + vty_out(vty, "%% Can't find rule.\n"); + return CMD_WARNING_CONFIG_FAILED; + case RMAP_COMPILE_ERROR: + vty_out(vty, "%% Argument is malformed.\n"); + return CMD_WARNING_CONFIG_FAILED; + case RMAP_COMPILE_SUCCESS: + /* + * Intentionally not handling these cases + */ + break; + } + + return CMD_SUCCESS; +} + +/* Delete rip route map rule. */ +static int eigrp_route_match_delete(struct vty *vty, + struct route_map_index *index, + const char *command, const char *arg) +{ + enum rmap_compile_rets ret; + + ret = route_map_delete_match(index, command, arg, type); + switch (ret) { + case RMAP_RULE_MISSING: + vty_out(vty, "%% Can't find rule.\n"); + return CMD_WARNING_CONFIG_FAILED; + case RMAP_COMPILE_ERROR: + vty_out(vty, "%% Argument is malformed.\n"); + return CMD_WARNING_CONFIG_FAILED; + case RMAP_COMPILE_SUCCESS: + /* + * These cases intentionally ignored + */ + break; + } + + return CMD_SUCCESS; +} + +/* Add eigrp route map rule. */ +static int eigrp_route_set_add(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg) +{ + enum rmap_compile_rets ret; + + ret = route_map_add_set(index, command, arg); + switch (ret) { + case RMAP_RULE_MISSING: + vty_out(vty, "%% Can't find rule.\n"); + return CMD_WARNING_CONFIG_FAILED; + case RMAP_COMPILE_ERROR: + /* + * rip, ripng and other protocols share the set metric command + * but only values from 0 to 16 are valid for rip and ripng + * if metric is out of range for rip and ripng, it is + * not for other protocols. Do not return an error + */ + if (strcmp(command, "metric")) { + vty_out(vty, "%% Argument is malformed.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + break; + case RMAP_COMPILE_SUCCESS: + /* + * These cases intentionally left blank here + */ + break; + } + + return CMD_SUCCESS; +} + +/* Delete eigrp route map rule. */ +static int eigrp_route_set_delete(struct vty *vty, + struct route_map_index *index, + const char *command, const char *arg) +{ + enum rmap_compile_rets ret; + + ret = route_map_delete_set(index, command, arg); + switch (ret) { + case RMAP_RULE_MISSING: + vty_out(vty, "%% Can't find rule.\n"); + return CMD_WARNING_CONFIG_FAILED; + case RMAP_COMPILE_ERROR: + vty_out(vty, "%% Argument is malformed.\n"); + return CMD_WARNING_CONFIG_FAILED; + case RMAP_COMPILE_SUCCESS: + /* + * These cases intentionally not handled + */ + break; + } + + return CMD_SUCCESS; +} + +/* Hook function for updating route_map assignment. */ +/* ARGSUSED */ +void eigrp_route_map_update(const char *notused) +{ + int i; + struct eigrp *e; + e = eigrp_lookup(); + + if (e) { + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { + if (e->route_map[i].name) + e->route_map[i].map = route_map_lookup_by_name( + e->route_map[i].name); + } + } +} + + +/* `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, struct prefix *prefix, route_map_object_t type, + void *object) +{ + // uint32_t *metric; + // uint32_t check; + // struct rip_info *rinfo; + // struct eigrp_route_descriptor *te; + // struct eigrp_prefix_descriptor *pe; + // struct listnode *node, *node2, *nnode, *nnode2; + // struct eigrp *e; + // + // e = eigrp_lookup(); + // + // if (type == RMAP_EIGRP) + // { + // 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; + // } + 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, struct prefix *prefix, + route_map_object_t type, void *object) +{ + // struct rip_info *rinfo; + // struct interface *ifp; + // char *ifname; + // + // if (type == RMAP_EIGRP) + // { + // ifname = rule; + // ifp = if_lookup_by_name(ifname); + // + // if (!ifp) + // return RMAP_NOMATCH; + // + // rinfo = object; + // + // /*if (rinfo->ifindex_out == ifp->ifindex || rinfo->ifindex == + // ifp->ifindex) + // return RMAP_MATCH; + // else + // return RMAP_NOMATCH;*/ + // } + 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, struct prefix *prefix, + route_map_object_t type, void *object) +{ + // struct access_list *alist; + // struct rip_info *rinfo; + // struct prefix_ipv4 p; + // + // if (type == RMAP_EIGRP) + // { + // rinfo = object; + // p.family = AF_INET; + // /*p.prefix = (rinfo->nexthop.s_addr) ? rinfo->nexthop : + // 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); + // } + return RMAP_NOMATCH; +} + +/* 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, struct prefix *prefix, + route_map_object_t type, void *object) +{ + // struct prefix_list *plist; + // struct rip_info *rinfo; + // struct prefix_ipv4 p; + // + // if (type == RMAP_EIGRP) + // { + // rinfo = object; + // p.family = AF_INET; + // /*p.prefix = (rinfo->nexthop.s_addr) ? rinfo->nexthop : + // 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); + // } + return RMAP_NOMATCH; +} + +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 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, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct access_list *alist; + + if (type == RMAP_EIGRP) { + 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); + } + return RMAP_NOMATCH; +} + +/* 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, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct prefix_list *plist; + + if (type == RMAP_EIGRP) { + 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); + } + return RMAP_NOMATCH; +} + +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, struct prefix *prefix, route_map_object_t type, + void *object) +{ + // unsigned short *tag; + // struct rip_info *rinfo; + // + // if (type == RMAP_EIGRP) + // { + // tag = rule; + // rinfo = object; + // + // /* The information stored by rinfo is host ordered. */ + // /*if (rinfo->tag == *tag) + // return RMAP_MATCH; + // else + // return RMAP_NOMATCH;*/ + // } + return RMAP_NOMATCH; +} + +/* Route map `match tag' match statement. `arg' is TAG value */ +static void *route_match_tag_compile(const char *arg) +{ + // unsigned short *tag; + // + // tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof(unsigned short)); + // *tag = atoi (arg); + // + // return tag; +} + +/* Free route map's compiled `match tag' value. */ +static void route_match_tag_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for tag matching. */ +static const struct route_map_rule_cmd route_match_tag_cmd = { + "tag", + route_match_tag, + route_match_tag_compile, + route_match_tag_free +}; + +/* Set metric to attribute. */ +static enum route_map_cmd_result_t +route_set_metric(void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + // if (type == RMAP_RIP) + // { + // struct rip_metric_modifier *mod; + // struct rip_info *rinfo; + // + // mod = rule; + // rinfo = object; + // + // /*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; + // int type; + // long metric; + // char *endptr = NULL; + // struct rip_metric_modifier *mod; + // + // len = strlen (arg); + // pnt = arg; + // + // if (len == 0) + // return NULL; + // + // /* Examine first character. */ + // if (arg[0] == '+') + // { + // //type = metric_increment; + // pnt++; + // } + // else if (arg[0] == '-') + // { + // //type = metric_decrement; + // pnt++; + // } + // /*else + // type = metric_absolute;*/ + // + // /* Check beginning with digit string. */ + // if (*pnt < '0' || *pnt > '9') + // return NULL; + // + // /* Convert string to integer. */ + // metric = strtol (pnt, &endptr, 10); + // + // if (metric == LONG_MAX || *endptr != '\0') + // return NULL; + // /*if (metric < 0 || metric > RIP_METRIC_INFINITY) + // return NULL;*/ + // + // mod = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, + // sizeof(struct rip_metric_modifier)); + // mod->type = type; + // mod->metric = metric; + + // 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, struct prefix *prefix, + route_map_object_t type, void *object) +{ + // struct in_addr *address; + // struct rip_info *rinfo; + // + // if(type == RMAP_RIP) + // { + // /* 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, struct prefix *prefix, + route_map_object_t type, void *object) +{ + // unsigned short *tag; + // struct rip_info *rinfo; + // + // if(type == RMAP_RIP) + // { + // /* Fetch routemap's rule information. */ + // tag = rule; + // rinfo = object; + // + // /* Set next hop value. */ + // rinfo->tag_out = *tag; + // } + + return RMAP_OKAY; +} + +/* Route map `tag' compile function. Given string is converted + to unsigned short. */ +static void *route_set_tag_compile(const char *arg) +{ + // unsigned short *tag; + // + // tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof(unsigned short)); + // *tag = atoi (arg); + // + // return tag; +} + +/* Free route map's compiled `ip nexthop' value. */ +static void route_set_tag_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for tag set. */ +static const struct route_map_rule_cmd route_set_tag_cmd = { + "tag", + route_set_tag, + route_set_tag_compile, + route_set_tag_free +}; + +#define MATCH_STR "Match values from routing table\n" +#define SET_STR "Set values in destination routing protocol\n" + +DEFUN (match_metric, + match_metric_cmd, + "match metric (0-4294967295)", + MATCH_STR + "Match metric of route\n" + "Metric value\n") +{ + return eigrp_route_match_add(vty, vty->index, "metric", argv[0]); +} + +DEFUN (no_match_metric, + no_match_metric_cmd, + "no match metric", + NO_STR + MATCH_STR + "Match metric of route\n") +{ + if (argc == 0) + return eigrp_route_match_delete(vty, vty->index, "metric", + NULL); + + return eigrp_route_match_delete(vty, vty->index, "metric", argv[0]); +} + +ALIAS(no_match_metric, no_match_metric_val_cmd, + "no match metric (0-4294967295)", NO_STR MATCH_STR + "Match metric of route\n" + "Metric value\n") + +DEFUN (match_interface, + match_interface_cmd, + "match interface WORD", + MATCH_STR + "Match first hop interface of route\n" + "Interface name\n") +{ + return eigrp_route_match_add(vty, vty->index, "interface", argv[0]); +} + +DEFUN (no_match_interface, + no_match_interface_cmd, + "no match interface", + NO_STR + MATCH_STR + "Match first hop interface of route\n") +{ + if (argc == 0) + return eigrp_route_match_delete(vty, vty->index, "interface", + NULL); + + return eigrp_route_match_delete(vty, vty->index, "interface", argv[0]); +} + +ALIAS(no_match_interface, no_match_interface_val_cmd, "no match interface WORD", + NO_STR MATCH_STR + "Match first hop interface of route\n" + "Interface name\n") + +DEFUN (match_ip_next_hop, + match_ip_next_hop_cmd, + "match ip next-hop ACCESSLIST4_NAME", + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "IP Access-list name\n") +{ + return eigrp_route_match_add(vty, vty->index, "ip next-hop", argv[0]); +} + +DEFUN (no_match_ip_next_hop, + no_match_ip_next_hop_cmd, + "no match ip next-hop", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n") +{ + if (argc == 0) + return eigrp_route_match_delete(vty, vty->index, "ip next-hop", + NULL); + + return eigrp_route_match_delete(vty, vty->index, "ip next-hop", + argv[0]); +} + +ALIAS(no_match_ip_next_hop, no_match_ip_next_hop_val_cmd, + "no match ip next-hop ACCESSLIST4_NAME", NO_STR MATCH_STR IP_STR + "Match next-hop address of route\n" + "IP Access-list name\n") + +DEFUN (match_ip_next_hop_prefix_list, + match_ip_next_hop_prefix_list_cmd, + "match ip next-hop prefix-list PREFIXLIST_NAME", + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + return eigrp_route_match_add(vty, vty->index, "ip next-hop prefix-list", + argv[0]); +} + +DEFUN (no_match_ip_next_hop_prefix_list, + no_match_ip_next_hop_prefix_list_cmd, + "no match ip next-hop prefix-list", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n") +{ + if (argc == 0) + return eigrp_route_match_delete( + vty, vty->index, "ip next-hop prefix-list", NULL); + + return eigrp_route_match_delete(vty, vty->index, + "ip next-hop prefix-list", argv[0]); +} + +ALIAS(no_match_ip_next_hop_prefix_list, + no_match_ip_next_hop_prefix_list_val_cmd, + "no match ip next-hop prefix-list PREFIXLIST_NAME", NO_STR MATCH_STR IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") + +DEFUN (match_ip_address, + match_ip_address_cmd, + "match ip address ACCESSLIST4_NAME", + MATCH_STR + IP_STR + "Match address of route\n" + "IP Access-list name\n") +{ + return eigrp_route_match_add(vty, vty->index, "ip address", argv[0]); +} + +DEFUN (no_match_ip_address, + no_match_ip_address_cmd, + "no match ip address", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n") +{ + if (argc == 0) + return eigrp_route_match_delete(vty, vty->index, "ip address", + NULL); + + return eigrp_route_match_delete(vty, vty->index, "ip address", argv[0]); +} + +ALIAS(no_match_ip_address, no_match_ip_address_val_cmd, + "no match ip address ACCESSLIST4_NAME", NO_STR MATCH_STR IP_STR + "Match address of route\n" + "IP Access-list name\n") + +DEFUN (match_ip_address_prefix_list, + match_ip_address_prefix_list_cmd, + "match ip address prefix-list PREFIXLIST_NAME", + MATCH_STR + IP_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + return eigrp_route_match_add(vty, vty->index, "ip address prefix-list", + argv[0]); +} + +DEFUN (no_match_ip_address_prefix_list, + no_match_ip_address_prefix_list_cmd, + "no match ip address prefix-list", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n" + "Match entries of prefix-lists\n") +{ + if (argc == 0) + return eigrp_route_match_delete(vty, vty->index, + "ip address prefix-list", NULL); + + return eigrp_route_match_delete(vty, vty->index, + "ip address prefix-list", argv[0]); +} + +ALIAS(no_match_ip_address_prefix_list, no_match_ip_address_prefix_list_val_cmd, + "no match ip address prefix-list PREFIXLIST_NAME", NO_STR MATCH_STR IP_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") + +DEFUN (match_tag, + match_tag_cmd, + "match tag (0-65535)", + MATCH_STR + "Match tag of route\n" + "Metric value\n") +{ + return eigrp_route_match_add(vty, vty->index, "tag", argv[0]); +} + +DEFUN (no_match_tag, + no_match_tag_cmd, + "no match tag", + NO_STR + MATCH_STR + "Match tag of route\n") +{ + if (argc == 0) + return eigrp_route_match_delete(vty, vty->index, "tag", NULL); + + return eigrp_route_match_delete(vty, vty->index, "tag", argv[0]); +} + +ALIAS(no_match_tag, no_match_tag_val_cmd, "no match tag (0-65535)", + NO_STR MATCH_STR + "Match tag of route\n" + "Metric value\n") + +/* set functions */ + +DEFUN (set_metric, + set_metric_cmd, + "set metric (0-4294967295)", + SET_STR + "Metric value for destination routing protocol\n" + "Metric value\n") +{ + return eigrp_route_set_add(vty, vty->index, "metric", argv[0]); +} + +ALIAS(set_metric, set_metric_addsub_cmd, "set metric <+/-metric>", SET_STR + "Metric value for destination routing protocol\n" + "Add or subtract metric\n") + +DEFUN (no_set_metric, + no_set_metric_cmd, + "no set metric", + NO_STR + SET_STR + "Metric value for destination routing protocol\n") +{ + if (argc == 0) + return eigrp_route_set_delete(vty, vty->index, "metric", NULL); + + return eigrp_route_set_delete(vty, vty->index, "metric", argv[0]); +} + +ALIAS(no_set_metric, no_set_metric_val_cmd, + "no set metric ((0-4294967295)|<+/-metric>)", NO_STR SET_STR + "Metric value for destination routing protocol\n" + "Metric value\n" + "Add or subtract metric\n") + +DEFUN (set_ip_nexthop, + set_ip_nexthop_cmd, + "set ip next-hop A.B.C.D", + SET_STR + IP_STR + "Next hop address\n" + "IP address of next hop\n") +{ + union sockunion su; + int ret; + + ret = str2sockunion(argv[0], &su); + if (ret < 0) { + vty_out(vty, "%% Malformed next-hop address\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + return eigrp_route_set_add(vty, vty->index, "ip next-hop", argv[0]); +} + +DEFUN (no_set_ip_nexthop, + no_set_ip_nexthop_cmd, + "no set ip next-hop", + NO_STR + SET_STR + IP_STR + "Next hop address\n") +{ + if (argc == 0) + return eigrp_route_set_delete(vty, vty->index, "ip next-hop", + NULL); + + return eigrp_route_set_delete(vty, vty->index, "ip next-hop", argv[0]); +} + +ALIAS(no_set_ip_nexthop, no_set_ip_nexthop_val_cmd, + "no set ip next-hop A.B.C.D", NO_STR SET_STR IP_STR + "Next hop address\n" + "IP address of next hop\n") + +DEFUN (set_tag, + set_tag_cmd, + "set tag (0-65535)", + SET_STR + "Tag value for routing protocol\n" + "Tag value\n") +{ + return eigrp_route_set_add(vty, vty->index, "tag", argv[0]); +} + +DEFUN (no_set_tag, + no_set_tag_cmd, + "no set tag", + NO_STR + SET_STR + "Tag value for routing protocol\n") +{ + if (argc == 0) + return eigrp_route_set_delete(vty, vty->index, "tag", NULL); + + return eigrp_route_set_delete(vty, vty->index, "tag", argv[0]); +} + +ALIAS(no_set_tag, no_set_tag_val_cmd, "no set tag (0-65535)", NO_STR SET_STR + "Tag value for routing protocol\n" + "Tag value\n") + +DEFUN (eigrp_distribute_list, + eigrp_distribute_list_cmd, + "distribute-list [prefix] ACCESSLIST_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 (eigrp_no_distribute_list, + eigrp_no_distribute_list_cmd, + "no distribute-list [prefix] ACCESSLIST_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); +} + + +/* Route-map init */ +void eigrp_route_map_init() +{ + route_map_init(); + route_map_init_vty(); + route_map_add_hook(eigrp_route_map_update); + route_map_delete_hook(eigrp_route_map_update); + + install_element(EIGRP_NODE, &eigrp_distribute_list_cmd); + install_element(EIGRP_NODE, &eigrp_no_distribute_list_cmd); + + /*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_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);*/ + + /*install_element (RMAP_NODE, &route_match_metric_cmd); + install_element (RMAP_NODE, &no_match_metric_cmd); + install_element (RMAP_NODE, &no_match_metric_val_cmd); + install_element (RMAP_NODE, &route_match_interface_cmd); + install_element (RMAP_NODE, &no_match_interface_cmd); + install_element (RMAP_NODE, &no_match_interface_val_cmd); + install_element (RMAP_NODE, &route_match_ip_next_hop_cmd); + install_element (RMAP_NODE, &no_match_ip_next_hop_cmd); + install_element (RMAP_NODE, &no_match_ip_next_hop_val_cmd); + install_element (RMAP_NODE, &route_match_ip_next_hop_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_next_hop_prefix_list_cmd); + install_element (RMAP_NODE, + &no_match_ip_next_hop_prefix_list_val_cmd);*/ + /*install_element (RMAP_NODE, &route_match_ip_address_cmd); + install_element (RMAP_NODE, &no_match_ip_address_cmd); + install_element (RMAP_NODE, &no_match_ip_address_val_cmd); + install_element (RMAP_NODE, &route_match_ip_address_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_address_prefix_list_cmd); + install_element (RMAP_NODE, + &no_match_ip_address_prefix_list_val_cmd);*/ + /*install_element (RMAP_NODE, &route_match_tag_cmd); + install_element (RMAP_NODE, &no_match_tag_cmd); + install_element (RMAP_NODE, &no_match_tag_val_cmd);*/ + + /*install_element (RMAP_NODE, &set_metric_cmd); + install_element (RMAP_NODE, &set_metric_addsub_cmd); + install_element (RMAP_NODE, &no_set_metric_cmd); + install_element (RMAP_NODE, &no_set_metric_val_cmd); + install_element (RMAP_NODE, &set_ip_nexthop_cmd); + install_element (RMAP_NODE, &no_set_ip_nexthop_cmd); + install_element (RMAP_NODE, &no_set_ip_nexthop_val_cmd); + install_element (RMAP_NODE, &set_tag_cmd); + install_element (RMAP_NODE, &no_set_tag_cmd); + install_element (RMAP_NODE, &no_set_tag_val_cmd);*/ +} diff --git a/eigrpd/eigrp_routemap.h b/eigrpd/eigrp_routemap.h new file mode 100644 index 0000000..c471679 --- /dev/null +++ b/eigrpd/eigrp_routemap.h @@ -0,0 +1,23 @@ +/* + * eigrp_routemap.h + * + * Created on: Nov 19, 2015 + * Author: root + */ + +#ifndef EIGRPD_EIGRP_ROUTEMAP_H_ +#define EIGRPD_EIGRP_ROUTEMAP_H_ + +#include "if_rmap.h" + +extern bool eigrp_routemap_prefix_apply(struct eigrp *eigrp, + struct eigrp_interface *ei, int in, + struct prefix *prefix); +extern void eigrp_route_map_update(const char *); +extern void eigrp_route_map_init(); +extern void eigrp_if_rmap_update(struct if_rmap *); +extern void eigrp_if_rmap_update_interface(struct interface *); +extern void eigrp_routemap_update_redistribute(void); +extern void eigrp_rmap_update(const char *); + +#endif /* EIGRPD_EIGRP_ROUTEMAP_H_ */ diff --git a/eigrpd/eigrp_siaquery.c b/eigrpd/eigrp_siaquery.c new file mode 100644 index 0000000..71486a1 --- /dev/null +++ b/eigrpd/eigrp_siaquery.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Sending and Receiving EIGRP SIA-Query Packets. + * Copyright (C) 2013-2014 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + */ + +#include <zebra.h> + +#include "frrevent.h" +#include "memory.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "sockunion.h" +#include "stream.h" +#include "log.h" +#include "sockopt.h" +#include "checksum.h" +#include "md5.h" +#include "vty.h" + +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_interface.h" +#include "eigrpd/eigrp_neighbor.h" +#include "eigrpd/eigrp_packet.h" +#include "eigrpd/eigrp_zebra.h" +#include "eigrpd/eigrp_vty.h" +#include "eigrpd/eigrp_dump.h" +#include "eigrpd/eigrp_macros.h" +#include "eigrpd/eigrp_topology.h" +#include "eigrpd/eigrp_fsm.h" + +/*EIGRP SIA-QUERY read function*/ +void eigrp_siaquery_receive(struct eigrp *eigrp, struct ip *iph, + struct eigrp_header *eigrph, struct stream *s, + struct eigrp_interface *ei, int size) +{ + struct eigrp_neighbor *nbr; + struct TLV_IPv4_Internal_type *tlv; + + uint16_t type; + + /* increment statistics. */ + ei->siaQuery_in++; + + /* get neighbor struct */ + nbr = eigrp_nbr_get(ei, eigrph, iph); + + /* neighbor must be valid, eigrp_nbr_get creates if none existed */ + assert(nbr); + + nbr->recv_sequence_number = ntohl(eigrph->sequence); + + while (s->endp > s->getp) { + type = stream_getw(s); + if (type == EIGRP_TLV_IPv4_INT) { + struct prefix dest_addr; + + stream_set_getp(s, s->getp - sizeof(uint16_t)); + + tlv = eigrp_read_ipv4_tlv(s); + + dest_addr.family = AFI_IP; + dest_addr.u.prefix4 = tlv->destination; + dest_addr.prefixlen = tlv->prefix_length; + struct eigrp_prefix_descriptor *dest = + eigrp_topology_table_lookup_ipv4( + eigrp->topology_table, &dest_addr); + + /* If the destination exists (it should, but one never + * know)*/ + if (dest != NULL) { + struct eigrp_fsm_action_message msg; + struct eigrp_route_descriptor *entry = + eigrp_route_descriptor_lookup( + dest->entries, nbr); + msg.packet_type = EIGRP_OPC_SIAQUERY; + msg.eigrp = eigrp; + msg.data_type = EIGRP_INT; + msg.adv_router = nbr; + msg.metrics = tlv->metric; + msg.entry = entry; + msg.prefix = dest; + eigrp_fsm_event(&msg); + } + eigrp_IPv4_InternalTLV_free(tlv); + } + } + eigrp_hello_send_ack(nbr); +} + +void eigrp_send_siaquery(struct eigrp_neighbor *nbr, + struct eigrp_prefix_descriptor *pe) +{ + struct eigrp_packet *ep; + uint16_t length = EIGRP_HEADER_LEN; + + ep = eigrp_packet_new(EIGRP_PACKET_MTU(nbr->ei->ifp->mtu), nbr); + + /* Prepare EIGRP INIT UPDATE header */ + eigrp_packet_header_init(EIGRP_OPC_SIAQUERY, nbr->ei->eigrp, ep->s, 0, + nbr->ei->eigrp->sequence_number, 0); + + // encode Authentication TLV, if needed + if ((nbr->ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) + && (nbr->ei->params.auth_keychain != NULL)) { + length += eigrp_add_authTLV_MD5_to_stream(ep->s, nbr->ei); + } + + length += eigrp_add_internalTLV_to_stream(ep->s, pe); + + if ((nbr->ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) + && (nbr->ei->params.auth_keychain != NULL)) { + eigrp_make_md5_digest(nbr->ei, ep->s, EIGRP_AUTH_UPDATE_FLAG); + } + + /* EIGRP Checksum */ + eigrp_packet_checksum(nbr->ei, ep->s, length); + + ep->length = length; + ep->dst.s_addr = nbr->src.s_addr; + + /*This ack number we await from neighbor*/ + ep->sequence_number = nbr->ei->eigrp->sequence_number; + + if (nbr->state == EIGRP_NEIGHBOR_UP) { + /*Put packet to retransmission queue*/ + eigrp_fifo_push(nbr->retrans_queue, ep); + + if (nbr->retrans_queue->count == 1) { + eigrp_send_packet_reliably(nbr); + } + } else + eigrp_packet_free(ep); +} diff --git a/eigrpd/eigrp_siareply.c b/eigrpd/eigrp_siareply.c new file mode 100644 index 0000000..6c8c1ef --- /dev/null +++ b/eigrpd/eigrp_siareply.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Sending and Receiving EIGRP SIA-Reply Packets. + * Copyright (C) 2013-2014 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + */ +#include <zebra.h> + +#include "frrevent.h" +#include "memory.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "sockunion.h" +#include "stream.h" +#include "log.h" +#include "sockopt.h" +#include "checksum.h" +#include "md5.h" +#include "vty.h" + +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_interface.h" +#include "eigrpd/eigrp_neighbor.h" +#include "eigrpd/eigrp_packet.h" +#include "eigrpd/eigrp_zebra.h" +#include "eigrpd/eigrp_vty.h" +#include "eigrpd/eigrp_dump.h" +#include "eigrpd/eigrp_macros.h" +#include "eigrpd/eigrp_topology.h" +#include "eigrpd/eigrp_fsm.h" + +/*EIGRP SIA-REPLY read function*/ +void eigrp_siareply_receive(struct eigrp *eigrp, struct ip *iph, + struct eigrp_header *eigrph, struct stream *s, + struct eigrp_interface *ei, int size) +{ + struct eigrp_neighbor *nbr; + struct TLV_IPv4_Internal_type *tlv; + + uint16_t type; + + /* increment statistics. */ + ei->siaReply_in++; + + /* get neighbor struct */ + nbr = eigrp_nbr_get(ei, eigrph, iph); + + /* neighbor must be valid, eigrp_nbr_get creates if none existed */ + assert(nbr); + + nbr->recv_sequence_number = ntohl(eigrph->sequence); + + while (s->endp > s->getp) { + type = stream_getw(s); + if (type == EIGRP_TLV_IPv4_INT) { + struct prefix dest_addr; + + stream_set_getp(s, s->getp - sizeof(uint16_t)); + + tlv = eigrp_read_ipv4_tlv(s); + + dest_addr.family = AFI_IP; + dest_addr.u.prefix4 = tlv->destination; + dest_addr.prefixlen = tlv->prefix_length; + struct eigrp_prefix_descriptor *dest = + eigrp_topology_table_lookup_ipv4( + eigrp->topology_table, &dest_addr); + + /* If the destination exists (it should, but one never + * know)*/ + if (dest != NULL) { + struct eigrp_fsm_action_message msg; + struct eigrp_route_descriptor *entry = + eigrp_route_descriptor_lookup( + dest->entries, nbr); + msg.packet_type = EIGRP_OPC_SIAQUERY; + msg.eigrp = eigrp; + msg.data_type = EIGRP_INT; + msg.adv_router = nbr; + msg.metrics = tlv->metric; + msg.entry = entry; + msg.prefix = dest; + eigrp_fsm_event(&msg); + } + eigrp_IPv4_InternalTLV_free(tlv); + } + } + eigrp_hello_send_ack(nbr); +} + +void eigrp_send_siareply(struct eigrp_neighbor *nbr, + struct eigrp_prefix_descriptor *pe) +{ + struct eigrp_packet *ep; + uint16_t length = EIGRP_HEADER_LEN; + + ep = eigrp_packet_new(EIGRP_PACKET_MTU(nbr->ei->ifp->mtu), nbr); + + /* Prepare EIGRP INIT UPDATE header */ + eigrp_packet_header_init(EIGRP_OPC_SIAREPLY, nbr->ei->eigrp, ep->s, 0, + nbr->ei->eigrp->sequence_number, 0); + + // encode Authentication TLV, if needed + if (nbr->ei->params.auth_type == EIGRP_AUTH_TYPE_MD5 + && nbr->ei->params.auth_keychain != NULL) { + length += eigrp_add_authTLV_MD5_to_stream(ep->s, nbr->ei); + } + + length += eigrp_add_internalTLV_to_stream(ep->s, pe); + + if ((nbr->ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) + && (nbr->ei->params.auth_keychain != NULL)) { + eigrp_make_md5_digest(nbr->ei, ep->s, EIGRP_AUTH_UPDATE_FLAG); + } + + /* EIGRP Checksum */ + eigrp_packet_checksum(nbr->ei, ep->s, length); + + ep->length = length; + ep->dst.s_addr = nbr->src.s_addr; + + /*This ack number we await from neighbor*/ + ep->sequence_number = nbr->ei->eigrp->sequence_number; + + if (nbr->state == EIGRP_NEIGHBOR_UP) { + /*Put packet to retransmission queue*/ + eigrp_fifo_push(nbr->retrans_queue, ep); + + if (nbr->retrans_queue->count == 1) { + eigrp_send_packet_reliably(nbr); + } + } else + eigrp_packet_free(ep); +} diff --git a/eigrpd/eigrp_snmp.c b/eigrpd/eigrp_snmp.c new file mode 100644 index 0000000..492ef3e --- /dev/null +++ b/eigrpd/eigrp_snmp.c @@ -0,0 +1,1312 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP SNMP Support. + * Copyright (C) 2013-2014 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + */ + +#include <zebra.h> + +#ifdef HAVE_SNMP +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> + +#include "frrevent.h" +#include "memory.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "sockunion.h" +#include "stream.h" +#include "log.h" +#include "sockopt.h" +#include "checksum.h" +#include "md5.h" +#include "keychain.h" +#include "smux.h" + +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_interface.h" +#include "eigrpd/eigrp_neighbor.h" +#include "eigrpd/eigrp_packet.h" +#include "eigrpd/eigrp_zebra.h" +#include "eigrpd/eigrp_vty.h" +#include "eigrpd/eigrp_dump.h" +#include "eigrpd/eigrp_network.h" +#include "eigrpd/eigrp_topology.h" +#include "eigrpd/eigrp_fsm.h" +#include "eigrpd/eigrp_snmp.h" + +struct list *eigrp_snmp_iflist; + +/* Declare static local variables for convenience. */ +SNMP_LOCAL_VARIABLES + +/* EIGRP-MIB - 1.3.6.1.4.1.9.9.449.1*/ +#define EIGRPMIB 1,3,6,1,4,1,9,9,449,1 + +/* EIGRP-MIB instances. */ +oid eigrp_oid[] = {EIGRPMIB}; + +/* EIGRP VPN entry */ +#define EIGRPVPNID 1 +#define EIGRPVPNNAME 2 + +/* EIGRP Traffic statistics entry */ +#define EIGRPASNUMBER 1 +#define EIGRPNBRCOUNT 2 +#define EIGRPHELLOSSENT 3 +#define EIGRPHELLOSRCVD 4 +#define EIGRPUPDATESSENT 5 +#define EIGRPUPDATESRCVD 6 +#define EIGRPQUERIESSENT 7 +#define EIGRPQUERIESRCVD 8 +#define EIGRPREPLIESSENT 9 +#define EIGRPREPLIESRCVD 10 +#define EIGRPACKSSENT 11 +#define EIGRPACKSRCVD 12 +#define EIGRPINPUTQHIGHMARK 13 +#define EIGRPINPUTQDROPS 14 +#define EIGRPSIAQUERIESSENT 15 +#define EIGRPSIAQUERIESRCVD 16 +#define EIGRPASROUTERIDTYPE 17 +#define EIGRPASROUTERID 18 +#define EIGRPTOPOROUTES 19 +#define EIGRPHEADSERIAL 20 +#define EIGRPNEXTSERIAL 21 +#define EIGRPXMITPENDREPLIES 22 +#define EIGRPXMITDUMMIES 23 + +/* EIGRP topology entry */ +#define EIGRPDESTNETTYPE 1 +#define EIGRPDESTNET 2 +#define EIGRPDESTNETPREFIXLEN 4 +#define EIGRPACTIVE 5 +#define EIGRPSTUCKINACTIVE 6 +#define EIGRPDESTSUCCESSORS 7 +#define EIGRPFDISTANCE 8 +#define EIGRPROUTEORIGINTYPE 9 +#define EIGRPROUTEORIGINADDRTYPE 10 +#define EIGRPROUTEORIGINADDR 11 +#define EIGRPNEXTHOPADDRESSTYPE 12 +#define EIGRPNEXTHOPADDRESS 13 +#define EIGRPNEXTHOPINTERFACE 14 +#define EIGRPDISTANCE 15 +#define EIGRPREPORTDISTANCE 16 + +/* EIGRP peer entry */ +#define EIGRPHANDLE 1 +#define EIGRPPEERADDRTYPE 2 +#define EIGRPPEERADDR 3 +#define EIGRPPEERIFINDEX 4 +#define EIGRPHOLDTIME 5 +#define EIGRPUPTIME 6 +#define EIGRPSRTT 7 +#define EIGRPRTO 8 +#define EIGRPPKTSENQUEUED 9 +#define EIGRPLASTSEQ 10 +#define EIGRPVERSION 11 +#define EIGRPRETRANS 12 +#define EIGRPRETRIES 13 + +/* EIGRP interface entry */ +#define EIGRPPEERCOUNT 3 +#define EIGRPXMITRELIABLEQ 4 +#define EIGRPXMITUNRELIABLEQ 5 +#define EIGRPMEANSRTT 6 +#define EIGRPPACINGRELIABLE 7 +#define EIGRPPACINGUNRELIABLE 8 +#define EIGRPMFLOWTIMER 9 +#define EIGRPPENDINGROUTES 10 +#define EIGRPHELLOINTERVAL 11 +#define EIGRPXMITNEXTSERIAL 12 +#define EIGRPUMCASTS 13 +#define EIGRPRMCASTS 14 +#define EIGRPUUCASTS 15 +#define EIGRPRUCASTS 16 +#define EIGRPMCASTEXCEPTS 17 +#define EIGRPCRPKTS 18 +#define EIGRPACKSSUPPRESSED 19 +#define EIGRPRETRANSSENT 20 +#define EIGRPOOSRCVD 21 +#define EIGRPAUTHMODE 22 +#define EIGRPAUTHKEYCHAIN 23 + +/* SNMP value hack. */ +#define COUNTER ASN_COUNTER +#define INTEGER ASN_INTEGER +#define GAUGE ASN_GAUGE +#define TIMETICKS ASN_TIMETICKS +#define IPADDRESS ASN_IPADDRESS +#define STRING ASN_OCTET_STR +#define IPADDRESSPREFIXLEN ASN_INTEGER +#define IPADDRESSTYPE ASN_INTEGER +#define INTERFACEINDEXORZERO ASN_INTEGER +#define UINTEGER ASN_UNSIGNED + +/* Hook functions. */ +static uint8_t *eigrpVpnEntry(struct variable *, oid *, size_t *, int, size_t *, + WriteMethod **); +static uint8_t *eigrpTraffStatsEntry(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static uint8_t *eigrpTopologyEntry(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static uint8_t *eigrpPeerEntry(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static uint8_t *eigrpInterfaceEntry(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); + + +struct variable eigrp_variables[] = { + /* EIGRP vpn variables */ + {EIGRPVPNID, INTEGER, NOACCESS, eigrpVpnEntry, 4, {1, 1, 1, 1}}, + {EIGRPVPNNAME, STRING, RONLY, eigrpVpnEntry, 4, {1, 1, 1, 2}}, + + /* EIGRP traffic stats variables */ + {EIGRPASNUMBER, + UINTEGER, + NOACCESS, + eigrpTraffStatsEntry, + 4, + {2, 1, 1, 1}}, + {EIGRPNBRCOUNT, UINTEGER, RONLY, eigrpTraffStatsEntry, 4, {2, 1, 1, 2}}, + {EIGRPHELLOSSENT, + COUNTER, + RONLY, + eigrpTraffStatsEntry, + 4, + {2, 1, 1, 3}}, + {EIGRPHELLOSRCVD, + COUNTER, + RONLY, + eigrpTraffStatsEntry, + 4, + {2, 1, 1, 4}}, + {EIGRPUPDATESSENT, + COUNTER, + RONLY, + eigrpTraffStatsEntry, + 4, + {2, 1, 1, 5}}, + {EIGRPUPDATESRCVD, + COUNTER, + RONLY, + eigrpTraffStatsEntry, + 4, + {2, 1, 1, 6}}, + {EIGRPQUERIESSENT, + COUNTER, + RONLY, + eigrpTraffStatsEntry, + 4, + {2, 1, 1, 7}}, + {EIGRPQUERIESRCVD, + COUNTER, + RONLY, + eigrpTraffStatsEntry, + 4, + {2, 1, 1, 8}}, + {EIGRPREPLIESSENT, + COUNTER, + RONLY, + eigrpTraffStatsEntry, + 4, + {2, 1, 1, 9}}, + {EIGRPREPLIESRCVD, + COUNTER, + RONLY, + eigrpTraffStatsEntry, + 4, + {2, 1, 1, 10}}, + {EIGRPACKSSENT, COUNTER, RONLY, eigrpTraffStatsEntry, 4, {2, 1, 1, 11}}, + {EIGRPACKSRCVD, COUNTER, RONLY, eigrpTraffStatsEntry, 4, {2, 1, 1, 12}}, + {EIGRPINPUTQHIGHMARK, + INTEGER, + RONLY, + eigrpTraffStatsEntry, + 4, + {2, 1, 1, 13}}, + {EIGRPINPUTQDROPS, + COUNTER, + RONLY, + eigrpTraffStatsEntry, + 4, + {2, 1, 1, 14}}, + {EIGRPSIAQUERIESSENT, + COUNTER, + RONLY, + eigrpTraffStatsEntry, + 4, + {2, 1, 1, 15}}, + {EIGRPSIAQUERIESRCVD, + COUNTER, + RONLY, + eigrpTraffStatsEntry, + 4, + {2, 1, 1, 16}}, + {EIGRPASROUTERIDTYPE, + IPADDRESSTYPE, + RONLY, + eigrpTraffStatsEntry, + 4, + {2, 1, 1, 17}}, + {EIGRPASROUTERID, + IPADDRESS, + RONLY, + eigrpTraffStatsEntry, + 4, + {2, 1, 1, 18}}, + {EIGRPTOPOROUTES, + COUNTER, + RONLY, + eigrpTraffStatsEntry, + 4, + {2, 1, 1, 19}}, + {EIGRPHEADSERIAL, + COUNTER, + RONLY, + eigrpTraffStatsEntry, + 4, + {2, 1, 1, 20}}, + {EIGRPNEXTSERIAL, + COUNTER, + RONLY, + eigrpTraffStatsEntry, + 4, + {2, 1, 1, 21}}, + {EIGRPXMITPENDREPLIES, + INTEGER, + RONLY, + eigrpTraffStatsEntry, + 4, + {2, 1, 1, 22}}, + {EIGRPXMITDUMMIES, + COUNTER, + RONLY, + eigrpTraffStatsEntry, + 4, + {2, 1, 1, 23}}, + + /* EIGRP topology variables */ + {EIGRPDESTNETTYPE, + IPADDRESSTYPE, + NOACCESS, + eigrpTopologyEntry, + 4, + {3, 1, 1, 1}}, + {EIGRPDESTNET, + IPADDRESSPREFIXLEN, + NOACCESS, + eigrpTopologyEntry, + 4, + {3, 1, 1, 2}}, + {EIGRPDESTNETPREFIXLEN, + IPADDRESSTYPE, + NOACCESS, + eigrpTopologyEntry, + 4, + {3, 1, 1, 4}}, + {EIGRPACTIVE, INTEGER, RONLY, eigrpTopologyEntry, 4, {3, 1, 1, 5}}, + {EIGRPSTUCKINACTIVE, + INTEGER, + RONLY, + eigrpTopologyEntry, + 4, + {3, 1, 1, 6}}, + {EIGRPDESTSUCCESSORS, + INTEGER, + RONLY, + eigrpTopologyEntry, + 4, + {3, 1, 1, 7}}, + {EIGRPFDISTANCE, INTEGER, RONLY, eigrpTopologyEntry, 4, {3, 1, 1, 8}}, + {EIGRPROUTEORIGINTYPE, + STRING, + RONLY, + eigrpTopologyEntry, + 4, + {3, 1, 1, 9}}, + {EIGRPROUTEORIGINADDRTYPE, + IPADDRESSTYPE, + RONLY, + eigrpTopologyEntry, + 4, + {3, 1, 1, 10}}, + {EIGRPROUTEORIGINADDR, + IPADDRESS, + RONLY, + eigrpTopologyEntry, + 4, + {3, 1, 1, 11}}, + {EIGRPNEXTHOPADDRESSTYPE, + IPADDRESSTYPE, + RONLY, + eigrpTopologyEntry, + 4, + {3, 1, 1, 12}}, + {EIGRPNEXTHOPADDRESS, + IPADDRESS, + RONLY, + eigrpTopologyEntry, + 4, + {3, 1, 1, 13}}, + {EIGRPNEXTHOPINTERFACE, + STRING, + RONLY, + eigrpTopologyEntry, + 4, + {3, 1, 1, 14}}, + {EIGRPDISTANCE, INTEGER, RONLY, eigrpTopologyEntry, 4, {3, 1, 1, 15}}, + {EIGRPREPORTDISTANCE, + INTEGER, + RONLY, + eigrpTopologyEntry, + 4, + {3, 1, 1, 16}}, + + /* EIGRP peer variables */ + {EIGRPHANDLE, INTEGER, NOACCESS, eigrpPeerEntry, 4, {4, 1, 1, 1}}, + {EIGRPPEERADDRTYPE, + IPADDRESSTYPE, + RONLY, + eigrpPeerEntry, + 4, + {4, 1, 1, 2}}, + {EIGRPPEERADDR, IPADDRESS, RONLY, eigrpPeerEntry, 4, {4, 1, 1, 3}}, + {EIGRPPEERIFINDEX, + INTERFACEINDEXORZERO, + RONLY, + eigrpPeerEntry, + 4, + {4, 1, 1, 4}}, + {EIGRPHOLDTIME, INTEGER, RONLY, eigrpPeerEntry, 4, {4, 1, 1, 5}}, + {EIGRPUPTIME, STRING, RONLY, eigrpPeerEntry, 4, {4, 1, 1, 6}}, + {EIGRPSRTT, INTEGER, RONLY, eigrpPeerEntry, 4, {4, 1, 1, 7}}, + {EIGRPRTO, INTEGER, RONLY, eigrpPeerEntry, 4, {4, 1, 1, 8}}, + {EIGRPPKTSENQUEUED, INTEGER, RONLY, eigrpPeerEntry, 4, {4, 1, 1, 9}}, + {EIGRPLASTSEQ, INTEGER, RONLY, eigrpPeerEntry, 4, {4, 1, 1, 10}}, + {EIGRPVERSION, STRING, RONLY, eigrpPeerEntry, 4, {4, 1, 1, 11}}, + {EIGRPRETRANS, COUNTER, RONLY, eigrpPeerEntry, 4, {4, 1, 1, 12}}, + {EIGRPRETRIES, INTEGER, RONLY, eigrpPeerEntry, 4, {4, 1, 1, 13}}, + + /* EIGRP interface variables */ + {EIGRPPEERCOUNT, GAUGE, RONLY, eigrpInterfaceEntry, 4, {5, 1, 1, 3}}, + {EIGRPXMITRELIABLEQ, + GAUGE, + RONLY, + eigrpInterfaceEntry, + 4, + {5, 1, 1, 4}}, + {EIGRPXMITUNRELIABLEQ, + GAUGE, + RONLY, + eigrpInterfaceEntry, + 4, + {5, 1, 1, 5}}, + {EIGRPMEANSRTT, INTEGER, RONLY, eigrpInterfaceEntry, 4, {5, 1, 1, 6}}, + {EIGRPPACINGRELIABLE, + INTEGER, + RONLY, + eigrpInterfaceEntry, + 4, + {5, 1, 1, 7}}, + {EIGRPPACINGUNRELIABLE, + INTEGER, + RONLY, + eigrpInterfaceEntry, + 4, + {5, 1, 1, 8}}, + {EIGRPMFLOWTIMER, INTEGER, RONLY, eigrpInterfaceEntry, 4, {5, 1, 1, 9}}, + {EIGRPPENDINGROUTES, + GAUGE, + RONLY, + eigrpInterfaceEntry, + 4, + {5, 1, 1, 10}}, + {EIGRPHELLOINTERVAL, + INTEGER, + RONLY, + eigrpInterfaceEntry, + 4, + {5, 1, 1, 11}}, + {EIGRPXMITNEXTSERIAL, + COUNTER, + RONLY, + eigrpInterfaceEntry, + 4, + {5, 1, 1, 12}}, + {EIGRPUMCASTS, COUNTER, RONLY, eigrpInterfaceEntry, 4, {5, 1, 1, 13}}, + {EIGRPRMCASTS, COUNTER, RONLY, eigrpInterfaceEntry, 4, {5, 1, 1, 14}}, + {EIGRPUUCASTS, COUNTER, RONLY, eigrpInterfaceEntry, 4, {5, 1, 1, 15}}, + {EIGRPRUCASTS, COUNTER, RONLY, eigrpInterfaceEntry, 4, {5, 1, 1, 16}}, + {EIGRPMCASTEXCEPTS, + COUNTER, + RONLY, + eigrpInterfaceEntry, + 4, + {5, 1, 1, 17}}, + {EIGRPCRPKTS, COUNTER, RONLY, eigrpInterfaceEntry, 4, {5, 1, 1, 18}}, + {EIGRPACKSSUPPRESSED, + COUNTER, + RONLY, + eigrpInterfaceEntry, + 4, + {5, 1, 1, 19}}, + {EIGRPRETRANSSENT, + COUNTER, + RONLY, + eigrpInterfaceEntry, + 4, + {5, 1, 1, 20}}, + {EIGRPOOSRCVD, COUNTER, RONLY, eigrpInterfaceEntry, 4, {5, 1, 1, 21}}, + {EIGRPAUTHMODE, INTEGER, RONLY, eigrpInterfaceEntry, 4, {5, 1, 1, 22}}, + {EIGRPAUTHKEYCHAIN, + STRING, + RONLY, + eigrpInterfaceEntry, + 4, + {5, 1, 1, 23}}}; + +static struct eigrp_neighbor *eigrp_snmp_nbr_lookup(struct eigrp *eigrp, + struct in_addr *nbr_addr, + unsigned int *ifindex) +{ + struct listnode *node, *nnode, *node2, *nnode2; + struct eigrp_interface *ei; + struct eigrp_neighbor *nbr; + + for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, ei)) { + for (ALL_LIST_ELEMENTS(ei->nbrs, node2, nnode2, nbr)) { + if (IPV4_ADDR_SAME(&nbr->src, nbr_addr)) { + return nbr; + } + } + } + return NULL; +} + +static struct eigrp_neighbor * +eigrp_snmp_nbr_lookup_next(struct in_addr *nbr_addr, unsigned int *ifindex, + int first) +{ + struct listnode *node, *nnode, *node2, *nnode2; + struct eigrp_interface *ei; + struct eigrp_neighbor *nbr; + struct eigrp_neighbor *min = NULL; + struct eigrp *eigrp; + + eigrp = eigrp_lookup(); + + for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, ei)) { + for (ALL_LIST_ELEMENTS(ei->nbrs, node2, nnode2, nbr)) { + if (first) { + if (!min) + min = nbr; + else if (ntohl(nbr->src.s_addr) + < ntohl(min->src.s_addr)) + min = nbr; + } else if (ntohl(nbr->src.s_addr) + > ntohl(nbr_addr->s_addr)) { + if (!min) + min = nbr; + else if (ntohl(nbr->src.s_addr) + < ntohl(min->src.s_addr)) + min = nbr; + } + } + } + if (min) { + *nbr_addr = min->src; + *ifindex = 0; + return min; + } + return NULL; +} + +static struct eigrp_neighbor *eigrpNbrLookup(struct variable *v, oid *name, + size_t *length, + struct in_addr *nbr_addr, + unsigned int *ifindex, int exact) +{ + unsigned int len; + int first; + struct eigrp_neighbor *nbr; + struct eigrp *eigrp; + + eigrp = eigrp_lookup(); + + if (!eigrp) + return NULL; + + if (exact) { + if (*length != v->namelen + IN_ADDR_SIZE + 1) + return NULL; + + oid2in_addr(name + v->namelen, IN_ADDR_SIZE, nbr_addr); + *ifindex = name[v->namelen + IN_ADDR_SIZE]; + + return eigrp_snmp_nbr_lookup(eigrp, nbr_addr, ifindex); + } else { + first = 0; + len = *length - v->namelen; + + if (len == 0) + first = 1; + + if (len > IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + + oid2in_addr(name + v->namelen, len, nbr_addr); + + len = *length - v->namelen - IN_ADDR_SIZE; + if (len >= 1) + *ifindex = name[v->namelen + IN_ADDR_SIZE]; + + nbr = eigrp_snmp_nbr_lookup_next(nbr_addr, ifindex, first); + + if (nbr) { + *length = v->namelen + IN_ADDR_SIZE + 1; + oid_copy_in_addr(name + v->namelen, nbr_addr); + name[v->namelen + IN_ADDR_SIZE] = *ifindex; + return nbr; + } + } + return NULL; +} + + +static uint8_t *eigrpVpnEntry(struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct eigrp *eigrp; + + eigrp = eigrp_lookup(); + + /* Check whether the instance identifier is valid */ + if (smux_header_generic(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) { + case EIGRPVPNID: /* 1 */ + /* The unique VPN identifier */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPVPNNAME: /* 2 */ + /* The name given to the VPN */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + default: + return NULL; + } + return NULL; +} + +static uint32_t eigrp_neighbor_count(struct eigrp *eigrp) +{ + uint32_t count; + struct eigrp_interface *ei; + struct listnode *node, *node2, *nnode2; + struct eigrp_neighbor *nbr; + + if (eigrp == NULL) { + return 0; + } + + count = 0; + for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { + for (ALL_LIST_ELEMENTS(ei->nbrs, node2, nnode2, nbr)) { + if (nbr->state == EIGRP_NEIGHBOR_UP) + count++; + } + } + + return count; +} + + +static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, + size_t *length, int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct eigrp *eigrp; + struct eigrp_interface *ei; + struct listnode *node, *nnode; + int counter; + + eigrp = eigrp_lookup(); + + /* Check whether the instance identifier is valid */ + if (smux_header_generic(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) { + case EIGRPASNUMBER: /* 1 */ + /* AS-number of this EIGRP instance. */ + if (eigrp) + return SNMP_INTEGER(eigrp->AS); + else + return SNMP_INTEGER(0); + case EIGRPNBRCOUNT: /* 2 */ + /* Neighbor count of this EIGRP instance */ + if (eigrp) + return SNMP_INTEGER(eigrp_neighbor_count(eigrp)); + else + return SNMP_INTEGER(0); + case EIGRPHELLOSSENT: /* 3 */ + /* Hello packets output count */ + if (eigrp) { + counter = 0; + for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, + ei)) { + counter += ei->hello_out; + } + return SNMP_INTEGER(counter); + } else + return SNMP_INTEGER(0); + case EIGRPHELLOSRCVD: /* 4 */ + /* Hello packets input count */ + if (eigrp) { + counter = 0; + for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, + ei)) { + counter += ei->hello_in; + } + return SNMP_INTEGER(counter); + } else + return SNMP_INTEGER(0); + case EIGRPUPDATESSENT: /* 5 */ + /* Update packets output count */ + if (eigrp) { + counter = 0; + for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, + ei)) { + counter += ei->update_out; + } + return SNMP_INTEGER(counter); + } else + return SNMP_INTEGER(0); + case EIGRPUPDATESRCVD: /* 6 */ + /* Update packets input count */ + if (eigrp) { + counter = 0; + for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, + ei)) { + counter += ei->update_in; + } + return SNMP_INTEGER(counter); + } else + return SNMP_INTEGER(0); + case EIGRPQUERIESSENT: /* 7 */ + /* Querry packets output count */ + if (eigrp) { + counter = 0; + for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, + ei)) { + counter += ei->query_out; + } + return SNMP_INTEGER(counter); + } else + return SNMP_INTEGER(0); + case EIGRPQUERIESRCVD: /* 8 */ + /* Querry packets input count */ + if (eigrp) { + counter = 0; + for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, + ei)) { + counter += ei->query_in; + } + return SNMP_INTEGER(counter); + } else + return SNMP_INTEGER(0); + case EIGRPREPLIESSENT: /* 9 */ + /* Reply packets output count */ + if (eigrp) { + counter = 0; + for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, + ei)) { + counter += ei->reply_out; + } + return SNMP_INTEGER(counter); + } else + return SNMP_INTEGER(0); + case EIGRPREPLIESRCVD: /* 10 */ + /* Reply packets input count */ + if (eigrp) { + counter = 0; + for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, + ei)) { + counter += ei->reply_in; + } + return SNMP_INTEGER(counter); + } else + return SNMP_INTEGER(0); + case EIGRPACKSSENT: /* 11 */ + /* Acknowledgement packets output count */ + if (eigrp) { + counter = 0; + for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, + ei)) { + counter += ei->ack_out; + } + return SNMP_INTEGER(counter); + } else + return SNMP_INTEGER(0); + case EIGRPACKSRCVD: /* 12 */ + /* Acknowledgement packets input count */ + if (eigrp) { + counter = 0; + for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, + ei)) { + counter += ei->ack_in; + } + return SNMP_INTEGER(counter); + } else + return SNMP_INTEGER(0); + case EIGRPINPUTQHIGHMARK: /* 13 */ + /* The highest number of EIGRP packets in the input queue */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPINPUTQDROPS: /* 14 */ + /* The number of EIGRP packets dropped from the input queue */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPSIAQUERIESSENT: /* 15 */ + /* SIA querry packets output count */ + if (eigrp) { + counter = 0; + for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, + ei)) { + counter += ei->siaQuery_out; + } + return SNMP_INTEGER(counter); + } else + return SNMP_INTEGER(0); + case EIGRPSIAQUERIESRCVD: /* 16 */ + /* SIA querry packets input count */ + if (eigrp) { + counter = 0; + for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, + ei)) { + counter += ei->siaQuery_in; + } + return SNMP_INTEGER(counter); + } else + return SNMP_INTEGER(0); + case EIGRPASROUTERIDTYPE: /* 17 */ + /* Whether the router ID is set manually or automatically */ + if (eigrp) + if (eigrp->router_id_static != 0) + return SNMP_INTEGER(1); + else + return SNMP_INTEGER(1); + else + return SNMP_INTEGER(0); + case EIGRPASROUTERID: /* 18 */ + /* Router ID for this EIGRP AS */ + if (eigrp) + if (eigrp->router_id_static != 0) + return SNMP_INTEGER(eigrp->router_id_static); + else + return SNMP_INTEGER(eigrp->router_id); + else + return SNMP_INTEGER(0); + case EIGRPTOPOROUTES: /* 19 */ + /* The total number of EIGRP derived routes currently existing + in the topology table for the AS */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPHEADSERIAL: /* 20 */ + /* The serial number of the first route in the internal + sequence for an AS*/ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPNEXTSERIAL: /* 21 */ + /* The serial number that would be assigned to the next new + or changed route in the topology table for the AS*/ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPXMITPENDREPLIES: /* 22 */ + /* Total number of outstanding replies expected to queries + that have been sent to peers in the current AS*/ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPXMITDUMMIES: /* 23 */ + /* Total number of currently existing dummies associated with + * the AS*/ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + default: + return NULL; + } + return NULL; +} + +static uint8_t *eigrpTopologyEntry(struct variable *v, oid *name, + size_t *length, int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct eigrp *eigrp; + + eigrp = eigrp_lookup(); + + /* Check whether the instance identifier is valid */ + if (smux_header_generic(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) { + case EIGRPDESTNETTYPE: /* 1 */ + /* The format of the destination IP network number for a single + route in the topology table*/ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPDESTNET: /* 2 */ + /* The destination IP network number for a single route in the + * topology table*/ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPDESTNETPREFIXLEN: /* 4 */ + /* The prefix length associated with the destination IP network + address + for a single route in the topology table in the AS*/ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPACTIVE: /* 5 */ + /* A value of true(1) indicates the route to the destination + network has failed + A value of false(2) indicates the route is stable + (passive).*/ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPSTUCKINACTIVE: /* 6 */ + /* A value of true(1) indicates that that this route which is in + active state + has not received any replies to queries for alternate paths + */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPDESTSUCCESSORS: /* 7 */ + /* Next routing hop for a path to the destination IP network */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPFDISTANCE: /* 8 */ + /* Minimum distance from this router to the destination IP + * network */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPROUTEORIGINTYPE: /* 9 */ + /* Text string describing the internal origin of the EIGRP route + */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPROUTEORIGINADDRTYPE: /* 10 */ + /* The format of the IP address defined as the origin of this + topology route entry */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPROUTEORIGINADDR: /* 11 */ + /* If the origin of the topology route entry is external to this + router, + then this object is the IP address of the router from which + it originated */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPNEXTHOPADDRESSTYPE: /* 12 */ + /* The format of the next hop IP address */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPNEXTHOPADDRESS: /* 13 */ + /* Next hop IP address for the route */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPNEXTHOPINTERFACE: /* 14 */ + /* The interface through which the next hop IP address is + * reached */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPDISTANCE: /* 15 */ + /* The computed distance to the destination network entry from + * this router */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPREPORTDISTANCE: /* 16 */ + /* The computed distance to the destination network in the + topology entry + reported to this router by the originator of this route */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + default: + return NULL; + } + return NULL; +} + +static uint8_t *eigrpPeerEntry(struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct eigrp *eigrp; + struct eigrp_interface *ei; + struct eigrp_neighbor *nbr; + struct in_addr nbr_addr; + unsigned int ifindex; + + eigrp = eigrp_lookup(); + + /* Check whether the instance identifier is valid */ + if (smux_header_generic(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + memset(&nbr_addr, 0, sizeof(nbr_addr)); + ifindex = 0; + + nbr = eigrpNbrLookup(v, name, length, &nbr_addr, &ifindex, exact); + if (!nbr) + return NULL; + ei = nbr->ei; + if (!ei) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) { + case EIGRPHANDLE: /* 1 */ + /* The unique internal identifier for the peer in the AS */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPPEERADDRTYPE: /* 2 */ + /* The format of the remote source IP address used by the peer + */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPPEERADDR: /* 3 */ + /* The source IP address used by the peer */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPPEERIFINDEX: /* 4 */ + /* The ifIndex of the interface on this router */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPHOLDTIME: /* 5 */ + /* How much time must pass without receiving a hello packet from + this + EIGRP peer before this router declares the peer down */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPUPTIME: /* 6 */ + /* The elapsed time since the EIGRP adjacency was first + * established */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPSRTT: /* 7 */ + /* The computed smooth round trip time for packets to and from + * the peer */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPRTO: /* 8 */ + /* The computed retransmission timeout for the peer */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPPKTSENQUEUED: /* 9 */ + /* The number of any EIGRP packets currently enqueued */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPLASTSEQ: /* 10 */ + /* sequence number of the last EIGRP packet sent to this peer */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPVERSION: /* 11 */ + /* The EIGRP version information reported by the remote peer */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPRETRANS: /* 12 */ + /* The cumulative number of retransmissions to this peer */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPRETRIES: /* 13 */ + /* The number of times the current unacknowledged packet has + * been retried */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + default: + return NULL; + } + return NULL; +} + +static uint8_t *eigrpInterfaceEntry(struct variable *v, oid *name, + size_t *length, int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct eigrp *eigrp; + struct listnode *node, *nnode; + struct keychain *keychain; + struct list *keylist; + + eigrp = eigrp_lookup(); + + /* Check whether the instance identifier is valid */ + if (smux_header_generic(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) { + case EIGRPPEERCOUNT: /* 3 */ + /* The number of EIGRP adjacencies currently formed with + peers reached through this interface */ + if (eigrp) { + return SNMP_INTEGER(eigrp_neighbor_count(eigrp)); + } else + return SNMP_INTEGER(0); + case EIGRPXMITRELIABLEQ: /* 4 */ + /* The number of EIGRP packets currently waiting in the reliable + transport transmission queue */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPXMITUNRELIABLEQ: /* 5 */ + /* The number of EIGRP packets currently waiting in the + unreliable + transport transmission queue */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPMEANSRTT: /* 6 */ + /* The average of all the computed smooth round trip time values + for a packet to and from all peers established on this + interface */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPPACINGRELIABLE: /* 7 */ + /* The configured time interval between EIGRP packet + * transmissions */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPPACINGUNRELIABLE: /* 8 */ + /* The configured time interval between EIGRP packet + transmissions + on the interface when the unreliable transport method is used + */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPMFLOWTIMER: /* 9 */ + /* The configured multicast flow control timer value */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPPENDINGROUTES: /* 10 */ + /* The number of queued EIGRP routing updates awaiting + * transmission */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPHELLOINTERVAL: /* 11 */ + /* The configured time interval between Hello packet + * transmissions */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPXMITNEXTSERIAL: /* 12 */ + /* The serial number of the next EIGRP packet that is to be + queued + for transmission */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPUMCASTS: /* 13 */ + /* The total number of unreliable EIGRP multicast packets sent + on this interface */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPRMCASTS: /* 14 */ + /* The total number of reliable EIGRP multicast packets sent + on this interface */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPUUCASTS: /* 15 */ + /* The total number of unreliable EIGRP unicast packets sent + on this interface */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPRUCASTS: /* 16 */ + /* The total number of reliable EIGRP unicast packets sent + on this interface */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPMCASTEXCEPTS: /* 17 */ + /* The total number of EIGRP multicast exception transmissions + */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPCRPKTS: /* 18 */ + /* The total number EIGRP Conditional-Receive packets sent on + * this interface */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPACKSSUPPRESSED: /* 19 */ + /* The total number of individual EIGRP acknowledgement packets + that have been + suppressed and combined in an already enqueued outbound + reliable packet on this interface */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPRETRANSSENT: /* 20 */ + /* The total number EIGRP packet retransmissions sent on the + * interface */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPOOSRCVD: /* 21 */ + /* The total number of out-of-sequence EIGRP packets received */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPAUTHMODE: /* 22 */ + /* The EIGRP authentication mode of the interface */ + if (eigrp) { + return SNMP_INTEGER(1); + } else + return SNMP_INTEGER(0); + case EIGRPAUTHKEYCHAIN: /* 23 */ + /* The name of the authentication key-chain configured + on this interface. */ + keylist = keychain_list_get(); + for (ALL_LIST_ELEMENTS(keylist, node, nnode, keychain)) { + return (uint8_t *)keychain->name; + } + if (eigrp && keychain) { + *var_len = str_len(keychain->name); + return (uint8_t *)keychain->name; + } else + return (uint8_t *)"TEST"; + break; + default: + return NULL; + } + return NULL; +} + +/* Register EIGRP-MIB. */ +void eigrp_snmp_init() +{ + eigrp_snmp_iflist = list_new(); + smux_init(eigrp_om->master); + REGISTER_MIB("ciscoEigrpMIB", eigrp_variables, variable, eigrp_oid); +} +#endif diff --git a/eigrpd/eigrp_snmp.h b/eigrpd/eigrp_snmp.h new file mode 100644 index 0000000..3930b5c --- /dev/null +++ b/eigrpd/eigrp_snmp.h @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP SNMP Support. + * Copyright (C) 2013-2014 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + */ + + +#ifndef _ZEBRA_EIGRP_SNMP_H +#define _ZEBRA_EIGRP_SNMP_H + +extern void eigrp_snmp_init(void); + + +#endif /* _ZEBRA_EIGRP_SNMP_H */ diff --git a/eigrpd/eigrp_structs.h b/eigrpd/eigrp_structs.h new file mode 100644 index 0000000..8735c48 --- /dev/null +++ b/eigrpd/eigrp_structs.h @@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Definition of Data Structures. + * Copyright (C) 2013-2016 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * Frantisek Gazo + * Tomas Hvorkovy + * Martin Kontsek + * Lukas Koribsky + */ + +#ifndef _ZEBRA_EIGRP_STRUCTS_H_ +#define _ZEBRA_EIGRP_STRUCTS_H_ + +#include "filter.h" + +#include "eigrpd/eigrp_const.h" +#include "eigrpd/eigrp_macros.h" + +struct eigrp_metrics { + uint32_t delay; + uint32_t bandwidth; + uint8_t mtu[3]; + uint8_t hop_count; + uint8_t reliability; + uint8_t load; + uint8_t tag; + uint8_t flags; +}; + +struct eigrp_extdata { + uint32_t orig; + uint32_t as; + uint32_t tag; + uint32_t metric; + uint16_t reserved; + uint8_t protocol; + uint8_t flags; +}; + +struct eigrp { + vrf_id_t vrf_id; + + uint16_t AS; /* Autonomous system number */ + uint16_t vrid; /* Virtual Router ID */ + uint8_t k_values[6]; /*Array for K values configuration*/ + uint8_t variance; /*Metric variance multiplier*/ + uint8_t max_paths; /*Maximum allowed paths for 1 prefix*/ + + /*Name of this EIGRP instance*/ + char *name; + + /* EIGRP Router ID. */ + struct in_addr router_id; /* Configured automatically. */ + struct in_addr router_id_static; /* Configured manually. */ + + struct list *eiflist; /* eigrp interfaces */ + uint8_t passive_interface_default; /* passive-interface default */ + + int fd; + unsigned int maxsndbuflen; + + uint32_t sequence_number; /*Global EIGRP sequence number*/ + + struct stream *ibuf; + struct list *oi_write_q; + + /*Threads*/ + struct event *t_write; + struct event *t_read; + struct event *t_distribute; /* timer for distribute list */ + + struct route_table *networks; /* EIGRP config networks. */ + + struct route_table *topology_table; + + uint64_t serno; /* Global serial number counter for topology entry + changes*/ + uint64_t serno_last_update; /* Highest serial number of information send + by last update*/ + struct list *topology_changes_internalIPV4; + struct list *topology_changes_externalIPV4; + + /*Neighbor self*/ + struct eigrp_neighbor *neighbor_self; + + /*Configured metric for redistributed routes*/ + struct eigrp_metrics dmetric[ZEBRA_ROUTE_MAX + 1]; + int redistribute; /* Num of redistributed protocols. */ + + /* Access-list. */ + struct access_list *list[EIGRP_FILTER_MAX]; + /* Prefix-list. */ + struct prefix_list *prefix[EIGRP_FILTER_MAX]; + /* Route-map. */ + struct route_map *routemap[EIGRP_FILTER_MAX]; + + /* For redistribute route map. */ + struct { + char *name; + struct route_map *map; + int metric_config; + uint32_t metric; + } route_map[ZEBRA_ROUTE_MAX]; + + /* distribute_ctx */ + struct distribute_ctx *distribute_ctx; + + QOBJ_FIELDS; +}; +DECLARE_QOBJ_TYPE(eigrp); + +struct eigrp_if_params { + uint8_t passive_interface; + uint32_t v_hello; + uint16_t v_wait; + uint8_t type; /* type of interface */ + uint32_t bandwidth; + uint32_t delay; + uint8_t reliability; + uint8_t load; + + char *auth_keychain; /* Associated keychain with interface*/ + int auth_type; /* EIGRP authentication type */ +}; + +enum { MEMBER_ALLROUTERS = 0, + MEMBER_MAX, +}; + +/*EIGRP interface structure*/ +struct eigrp_interface { + struct eigrp_if_params params; + + /*multicast group refcnts */ + bool member_allrouters; + + /* This interface's parent eigrp instance. */ + struct eigrp *eigrp; + + /* Interface data from zebra. */ + struct interface *ifp; + + /* Packet send buffer. */ + struct eigrp_fifo *obuf; /* Output queue */ + + /* To which multicast groups do we currently belong? */ + + uint32_t curr_bandwidth; + uint32_t curr_mtu; + + uint8_t multicast_memberships; + + /* EIGRP Network Type. */ + uint8_t type; + + struct prefix address; /* Interface prefix */ + + /* Neighbor information. */ + struct list *nbrs; /* EIGRP Neighbor List */ + + /* Threads. */ + struct event *t_hello; /* timer */ + struct event *t_distribute; /* timer for distribute list */ + + int on_write_q; + + /* Statistics fields. */ + uint32_t hello_in; /* Hello message input count. */ + uint32_t update_in; /* Update message input count. */ + uint32_t query_in; /* Querry message input count. */ + uint32_t reply_in; /* Reply message input count. */ + uint32_t hello_out; /* Hello message output count. */ + uint32_t update_out; /* Update message output count. */ + uint32_t query_out; /* Query message output count. */ + uint32_t reply_out; /* Reply message output count. */ + uint32_t siaQuery_in; + uint32_t siaQuery_out; + uint32_t siaReply_in; + uint32_t siaReply_out; + uint32_t ack_out; + uint32_t ack_in; + + uint32_t crypt_seqnum; /* Cryptographic Sequence Number */ + + /* Access-list. */ + struct access_list *list[EIGRP_FILTER_MAX]; + /* Prefix-list. */ + struct prefix_list *prefix[EIGRP_FILTER_MAX]; + /* Route-map. */ + struct route_map *routemap[EIGRP_FILTER_MAX]; +}; + +/* Determines if it is first or last packet + * when packet consists of multiple packet + * chunks because of many route TLV + * (all won't fit into one packet) */ +enum Packet_part_type { + EIGRP_PACKET_PART_NA, + EIGRP_PACKET_PART_FIRST, + EIGRP_PACKET_PART_LAST +}; + +/* Neighbor Data Structure */ +struct eigrp_neighbor { + /* This neighbor's parent eigrp interface. */ + struct eigrp_interface *ei; + + /* EIGRP neighbor Information */ + uint8_t state; /* neigbor status. */ + + uint32_t recv_sequence_number; /* Last received sequence Number. */ + uint32_t init_sequence_number; + + /*If packet is unacknowledged, we try to send it again 16 times*/ + uint8_t retrans_counter; + + struct in_addr src; /* Neighbor Src address. */ + + uint8_t os_rel_major; // system version - just for show + uint8_t os_rel_minor; // system version - just for show + uint8_t tlv_rel_major; // eigrp version - tells us what TLV format to + // use + uint8_t tlv_rel_minor; // eigrp version - tells us what TLV format to + // use + + uint8_t K1; + uint8_t K2; + uint8_t K3; + uint8_t K4; + uint8_t K5; + uint8_t K6; + + /* Timer values. */ + uint16_t v_holddown; + + /* Threads. */ + struct event *t_holddown; + struct event *t_nbr_send_gr; /* thread for sending multiple GR packet + chunks */ + + struct eigrp_fifo *retrans_queue; + struct eigrp_fifo *multicast_queue; + + uint32_t crypt_seqnum; /* Cryptographic Sequence Number. */ + + /* prefixes not received from neighbor during Graceful restart */ + struct list *nbr_gr_prefixes; + /* prefixes not yet send to neighbor during Graceful restart */ + struct list *nbr_gr_prefixes_send; + /* if packet is first or last during Graceful restart */ + enum Packet_part_type nbr_gr_packet_type; +}; + +//--------------------------------------------------------------------------------------------------------------------------------------------- + + +struct eigrp_packet { + struct eigrp_packet *next; + struct eigrp_packet *previous; + + /* Pointer to data stream. */ + struct stream *s; + + /* IP destination address. */ + struct in_addr dst; + + /*Packet retransmission thread*/ + struct event *t_retrans_timer; + + /*Packet retransmission counter*/ + uint8_t retrans_counter; + + uint32_t sequence_number; + + /* EIGRP packet length. */ + uint16_t length; + + struct eigrp_neighbor *nbr; +}; + +struct eigrp_fifo { + struct eigrp_packet *head; + struct eigrp_packet *tail; + + unsigned long count; +}; + +struct eigrp_header { + uint8_t version; + uint8_t opcode; + uint16_t checksum; + uint32_t flags; + uint32_t sequence; + uint32_t ack; + uint16_t vrid; + uint16_t ASNumber; + char *tlv[0]; + +} __attribute__((packed)); + + +/** + * Generic TLV type used for packet decoding. + * + * +-----+------------------+ + * | | | | + * | Type| Len | Vector | + * | | | | + * +-----+------------------+ + */ +struct eigrp_tlv_hdr_type { + uint16_t type; + uint16_t length; + uint8_t value[0]; +} __attribute__((packed)); + +struct TLV_Parameter_Type { + uint16_t type; + uint16_t length; + uint8_t K1; + uint8_t K2; + uint8_t K3; + uint8_t K4; + uint8_t K5; + uint8_t K6; + uint16_t hold_time; +} __attribute__((packed)); + +struct TLV_MD5_Authentication_Type { + uint16_t type; + uint16_t length; + uint16_t auth_type; + uint16_t auth_length; + uint32_t key_id; + uint32_t key_sequence; + uint8_t Nullpad[8]; + uint8_t digest[EIGRP_AUTH_TYPE_MD5_LEN]; + +} __attribute__((packed)); + +struct TLV_SHA256_Authentication_Type { + uint16_t type; + uint16_t length; + uint16_t auth_type; + uint16_t auth_length; + uint32_t key_id; + uint32_t key_sequence; + uint8_t Nullpad[8]; + uint8_t digest[EIGRP_AUTH_TYPE_SHA256_LEN]; + +} __attribute__((packed)); + +struct TLV_Sequence_Type { + uint16_t type; + uint16_t length; + uint8_t addr_length; + struct in_addr *addresses; +} __attribute__((packed)); + +struct TLV_Next_Multicast_Sequence { + uint16_t type; + uint16_t length; + uint32_t multicast_sequence; +} __attribute__((packed)); + +struct TLV_Software_Type { + uint16_t type; + uint16_t length; + uint8_t vender_major; + uint8_t vender_minor; + uint8_t eigrp_major; + uint8_t eigrp_minor; +} __attribute__((packed)); + +struct TLV_IPv4_Internal_type { + uint16_t type; + uint16_t length; + struct in_addr forward; + + /*Metrics*/ + struct eigrp_metrics metric; + + uint8_t prefix_length; + + struct in_addr destination; +} __attribute__((packed)); + +struct TLV_IPv4_External_type { + uint16_t type; + uint16_t length; + struct in_addr next_hop; + struct in_addr originating_router; + uint32_t originating_as; + uint32_t administrative_tag; + uint32_t external_metric; + uint16_t reserved; + uint8_t external_protocol; + uint8_t external_flags; + + /*Metrics*/ + struct eigrp_metrics metric; + + uint8_t prefix_length; + unsigned char destination_part[4]; + struct in_addr destination; +} __attribute__((packed)); + +/* EIGRP Peer Termination TLV - used for hard restart */ +struct TLV_Peer_Termination_type { + uint16_t type; + uint16_t length; + uint8_t unknown; + uint32_t neighbor_ip; +} __attribute__((packed)); + +/* Who executed Graceful restart */ +enum GR_type { EIGRP_GR_MANUAL, EIGRP_GR_FILTER }; + +//--------------------------------------------------------------------------------------------------------------------------------------------- + +/* EIGRP Topology table node structure */ +struct eigrp_prefix_descriptor { + struct list *entries, *rij; + uint32_t fdistance; // FD + uint32_t rdistance; // RD + uint32_t distance; // D + struct eigrp_metrics reported_metric; // RD for sending + + uint8_t nt; // network type + uint8_t state; // route fsm state + uint8_t af; // address family + uint8_t req_action; // required action + + struct prefix *destination; + + // If network type is REMOTE_EXTERNAL, pointer will have reference to + // its external TLV + struct TLV_IPv4_External_type *extTLV; + + uint64_t serno; /*Serial number for this entry. Increased with each + change of entry*/ +}; + +/* EIGRP Topology table record structure */ +struct eigrp_route_descriptor { + uint16_t type; + uint16_t afi; + + struct eigrp_prefix_descriptor *prefix; + struct eigrp_neighbor *adv_router; + struct in_addr nexthop; + + uint32_t reported_distance; // distance reported by neighbor + uint32_t distance; // sum of reported distance and link cost to + // advertised neighbor + + struct eigrp_metrics reported_metric; + struct eigrp_metrics total_metric; + + struct eigrp_metrics metric; + struct eigrp_extdata extdata; + + uint8_t flags; // used for marking successor and FS + + struct eigrp_interface *ei; // pointer for case of connected entry +}; + +//--------------------------------------------------------------------------------------------------------------------------------------------- +typedef enum { + EIGRP_CONNECTED, + EIGRP_INT, + EIGRP_EXT, +} msg_data_t; + +/* EIGRP Finite State Machine */ + +struct eigrp_fsm_action_message { + uint8_t packet_type; // UPDATE, QUERY, SIAQUERY, SIAREPLY + struct eigrp *eigrp; // which thread sent mesg + struct eigrp_neighbor *adv_router; // advertising neighbor + struct eigrp_route_descriptor *entry; + struct eigrp_prefix_descriptor *prefix; + msg_data_t data_type; // internal or external tlv type + struct eigrp_metrics metrics; + enum metric_change change; +}; + +#endif /* _ZEBRA_EIGRP_STRUCTURES_H_ */ diff --git a/eigrpd/eigrp_topology.c b/eigrpd/eigrp_topology.c new file mode 100644 index 0000000..f17be8f --- /dev/null +++ b/eigrpd/eigrp_topology.c @@ -0,0 +1,530 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Topology Table. + * Copyright (C) 2013-2016 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * Frantisek Gazo + * Tomas Hvorkovy + * Martin Kontsek + * Lukas Koribsky + */ + +#include <zebra.h> + +#include "prefix.h" +#include "table.h" +#include "memory.h" +#include "log.h" +#include "linklist.h" +#include "vty.h" +#include "lib_errors.h" + +#include "eigrpd/eigrp_types.h" +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_interface.h" +#include "eigrpd/eigrp_neighbor.h" +#include "eigrpd/eigrp_packet.h" +#include "eigrpd/eigrp_zebra.h" +#include "eigrpd/eigrp_vty.h" +#include "eigrpd/eigrp_network.h" +#include "eigrpd/eigrp_dump.h" +#include "eigrpd/eigrp_topology.h" +#include "eigrpd/eigrp_fsm.h" +#include "eigrpd/eigrp_metric.h" + +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_ROUTE_DESCRIPTOR, "EIGRP Nexthop Entry"); +DEFINE_MTYPE(EIGRPD, EIGRP_PREFIX_DESCRIPTOR, "EIGRP Prefix"); + +static int eigrp_route_descriptor_cmp(struct eigrp_route_descriptor *rd1, + struct eigrp_route_descriptor *rd2); + +/* + * Returns linkedlist used as topology table + * cmp - assigned function for comparing topology nodes + * del - assigned function executed before deleting topology node by list + * function + */ +struct route_table *eigrp_topology_new(void) +{ + return route_table_init(); +} + +/* + * Returns new created toplogy node + * cmp - assigned function for comparing topology entry + */ +struct eigrp_prefix_descriptor *eigrp_prefix_descriptor_new(void) +{ + struct eigrp_prefix_descriptor *new; + new = XCALLOC(MTYPE_EIGRP_PREFIX_DESCRIPTOR, + sizeof(struct eigrp_prefix_descriptor)); + new->entries = list_new(); + new->rij = list_new(); + new->entries->cmp = (int (*)(void *, void *))eigrp_route_descriptor_cmp; + new->distance = new->fdistance = new->rdistance = EIGRP_MAX_METRIC; + new->destination = NULL; + + return new; +} + +/* + * Topology entry comparison + */ +static int eigrp_route_descriptor_cmp(struct eigrp_route_descriptor *entry1, + struct eigrp_route_descriptor *entry2) +{ + if (entry1->distance < entry2->distance) + return -1; + if (entry1->distance > entry2->distance) + return 1; + + return 0; +} + +/* + * Returns new topology entry + */ + +struct eigrp_route_descriptor *eigrp_route_descriptor_new(void) +{ + struct eigrp_route_descriptor *new; + + new = XCALLOC(MTYPE_EIGRP_ROUTE_DESCRIPTOR, + sizeof(struct eigrp_route_descriptor)); + new->reported_distance = EIGRP_MAX_METRIC; + new->distance = EIGRP_MAX_METRIC; + + return new; +} + +/* + * Freeing topology table list + */ +void eigrp_topology_free(struct eigrp *eigrp, struct route_table *table) +{ + eigrp_topology_delete_all(eigrp, table); + route_table_finish(table); +} + +/* + * Adding topology node to topology table + */ +void eigrp_prefix_descriptor_add(struct route_table *topology, + struct eigrp_prefix_descriptor *pe) +{ + struct route_node *rn; + + rn = route_node_get(topology, pe->destination); + if (rn->info) { + if (IS_DEBUG_EIGRP_EVENT) + zlog_debug( + "%s: %pFX Should we have found this entry in the topo table?", + __func__, pe->destination); + route_unlock_node(rn); + } + + rn->info = pe; +} + +/* + * Adding topology entry to topology node + */ +void eigrp_route_descriptor_add(struct eigrp *eigrp, + struct eigrp_prefix_descriptor *node, + struct eigrp_route_descriptor *entry) +{ + struct list *l = list_new(); + + listnode_add(l, entry); + + if (listnode_lookup(node->entries, entry) == NULL) { + listnode_add_sort(node->entries, entry); + entry->prefix = node; + + eigrp_zebra_route_add(eigrp, node->destination, + l, node->fdistance); + } + + list_delete(&l); +} + +/* + * Deleting topology node from topology table + */ +void eigrp_prefix_descriptor_delete(struct eigrp *eigrp, + struct route_table *table, + struct eigrp_prefix_descriptor *pe) +{ + struct eigrp_route_descriptor *ne; + struct listnode *node, *nnode; + struct route_node *rn; + + if (!eigrp) + return; + + rn = route_node_lookup(table, pe->destination); + if (!rn) + return; + + /* + * Emergency removal of the node from this list. + * Whatever it is. + */ + listnode_delete(eigrp->topology_changes_internalIPV4, pe); + + for (ALL_LIST_ELEMENTS(pe->entries, node, nnode, ne)) + eigrp_route_descriptor_delete(eigrp, pe, ne); + list_delete(&pe->entries); + list_delete(&pe->rij); + eigrp_zebra_route_delete(eigrp, pe->destination); + prefix_free(&pe->destination); + + rn->info = NULL; + route_unlock_node(rn); // Lookup above + route_unlock_node(rn); // Initial creation + XFREE(MTYPE_EIGRP_PREFIX_DESCRIPTOR, pe); +} + +/* + * Deleting topology entry from topology node + */ +void eigrp_route_descriptor_delete(struct eigrp *eigrp, + struct eigrp_prefix_descriptor *node, + struct eigrp_route_descriptor *entry) +{ + if (listnode_lookup(node->entries, entry) != NULL) { + listnode_delete(node->entries, entry); + eigrp_zebra_route_delete(eigrp, node->destination); + XFREE(MTYPE_EIGRP_ROUTE_DESCRIPTOR, entry); + } +} + +/* + * Deleting all nodes from topology table + */ +void eigrp_topology_delete_all(struct eigrp *eigrp, + struct route_table *topology) +{ + struct route_node *rn; + struct eigrp_prefix_descriptor *pe; + + for (rn = route_top(topology); rn; rn = route_next(rn)) { + pe = rn->info; + + if (!pe) + continue; + + eigrp_prefix_descriptor_delete(eigrp, topology, pe); + } +} + +struct eigrp_prefix_descriptor * +eigrp_topology_table_lookup_ipv4(struct route_table *table, + struct prefix *address) +{ + struct eigrp_prefix_descriptor *pe; + struct route_node *rn; + + rn = route_node_lookup(table, address); + if (!rn) + return NULL; + + pe = rn->info; + + route_unlock_node(rn); + + return pe; +} + +/* + * For a future optimization, put the successor list into it's + * own separate list from the full list? + * + * That way we can clean up all the list_new and list_delete's + * that we are doing. DBS + */ +struct list * +eigrp_topology_get_successor(struct eigrp_prefix_descriptor *table_node) +{ + struct list *successors = list_new(); + struct eigrp_route_descriptor *data; + struct listnode *node1, *node2; + + for (ALL_LIST_ELEMENTS(table_node->entries, node1, node2, data)) { + if (data->flags & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG) { + listnode_add(successors, data); + } + } + + /* + * If we have no successors return NULL + */ + if (!successors->count) { + list_delete(&successors); + successors = NULL; + } + + return successors; +} + +struct list * +eigrp_topology_get_successor_max(struct eigrp_prefix_descriptor *table_node, + unsigned int maxpaths) +{ + struct list *successors = eigrp_topology_get_successor(table_node); + + if (successors && successors->count > maxpaths) { + do { + struct listnode *node = listtail(successors); + + list_delete_node(successors, node); + + } while (successors->count > maxpaths); + } + + return successors; +} + +struct eigrp_route_descriptor * +eigrp_route_descriptor_lookup(struct list *entries, struct eigrp_neighbor *nbr) +{ + struct eigrp_route_descriptor *data; + struct listnode *node, *nnode; + for (ALL_LIST_ELEMENTS(entries, node, nnode, data)) { + if (data->adv_router == nbr) { + return data; + } + } + + return NULL; +} + +/* Lookup all prefixes from specified neighbor */ +struct list *eigrp_neighbor_prefixes_lookup(struct eigrp *eigrp, + struct eigrp_neighbor *nbr) +{ + struct listnode *node2, *node22; + struct eigrp_route_descriptor *entry; + struct eigrp_prefix_descriptor *pe; + struct route_node *rn; + + /* create new empty list for prefixes storage */ + struct list *prefixes = list_new(); + + /* iterate over all prefixes in topology table */ + for (rn = route_top(eigrp->topology_table); rn; rn = route_next(rn)) { + if (!rn->info) + continue; + pe = rn->info; + /* iterate over all neighbor entry in prefix */ + for (ALL_LIST_ELEMENTS(pe->entries, node2, node22, entry)) { + /* if entry is from specified neighbor, add to list */ + if (entry->adv_router == nbr) { + listnode_add(prefixes, pe); + } + } + } + + /* return list of prefixes from specified neighbor */ + return prefixes; +} + +enum metric_change +eigrp_topology_update_distance(struct eigrp_fsm_action_message *msg) +{ + struct eigrp *eigrp = msg->eigrp; + struct eigrp_prefix_descriptor *prefix = msg->prefix; + struct eigrp_route_descriptor *entry = msg->entry; + enum metric_change change = METRIC_SAME; + uint32_t new_reported_distance; + + assert(entry); + + switch (msg->data_type) { + case EIGRP_CONNECTED: + if (prefix->nt == EIGRP_TOPOLOGY_TYPE_CONNECTED) + return change; + + change = METRIC_DECREASE; + break; + case EIGRP_INT: + if (prefix->nt == EIGRP_TOPOLOGY_TYPE_CONNECTED) { + change = METRIC_INCREASE; + goto distance_done; + } + if (eigrp_metrics_is_same(msg->metrics, + entry->reported_metric)) { + return change; // No change + } + + new_reported_distance = + eigrp_calculate_metrics(eigrp, msg->metrics); + + if (entry->reported_distance < new_reported_distance) { + change = METRIC_INCREASE; + goto distance_done; + } else + change = METRIC_DECREASE; + + entry->reported_metric = msg->metrics; + entry->reported_distance = new_reported_distance; + eigrp_calculate_metrics(eigrp, msg->metrics); + entry->distance = eigrp_calculate_total_metrics(eigrp, entry); + break; + case EIGRP_EXT: + if (prefix->nt == EIGRP_TOPOLOGY_TYPE_REMOTE_EXTERNAL) { + if (eigrp_metrics_is_same(msg->metrics, + entry->reported_metric)) + return change; + } else { + change = METRIC_INCREASE; + goto distance_done; + } + break; + default: + flog_err(EC_LIB_DEVELOPMENT, "%s: Please implement handler", + __func__); + break; + } +distance_done: + /* + * Move to correct position in list according to new distance + */ + listnode_delete(prefix->entries, entry); + listnode_add_sort(prefix->entries, entry); + + return change; +} + +void eigrp_topology_update_all_node_flags(struct eigrp *eigrp) +{ + struct eigrp_prefix_descriptor *pe; + struct route_node *rn; + + if (!eigrp) + return; + + for (rn = route_top(eigrp->topology_table); rn; rn = route_next(rn)) { + pe = rn->info; + + if (!pe) + continue; + + eigrp_topology_update_node_flags(eigrp, pe); + } +} + +void eigrp_topology_update_node_flags(struct eigrp *eigrp, + struct eigrp_prefix_descriptor *dest) +{ + struct listnode *node; + struct eigrp_route_descriptor *entry; + + for (ALL_LIST_ELEMENTS_RO(dest->entries, node, entry)) { + if (entry->reported_distance < dest->fdistance) { + // is feasible successor, can be successor + if (((uint64_t)entry->distance + <= (uint64_t)dest->distance + * (uint64_t)eigrp->variance) + && entry->distance != EIGRP_MAX_METRIC) { + // is successor + entry->flags |= + EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG; + entry->flags &= + ~EIGRP_ROUTE_DESCRIPTOR_FSUCCESSOR_FLAG; + } else { + // is feasible successor only + entry->flags |= + EIGRP_ROUTE_DESCRIPTOR_FSUCCESSOR_FLAG; + entry->flags &= + ~EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG; + } + } else { + entry->flags &= ~EIGRP_ROUTE_DESCRIPTOR_FSUCCESSOR_FLAG; + entry->flags &= ~EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG; + } + } +} + +void eigrp_update_routing_table(struct eigrp *eigrp, + struct eigrp_prefix_descriptor *prefix) +{ + struct list *successors; + struct listnode *node; + struct eigrp_route_descriptor *entry; + + successors = eigrp_topology_get_successor_max(prefix, eigrp->max_paths); + + if (successors) { + eigrp_zebra_route_add(eigrp, prefix->destination, successors, + prefix->fdistance); + for (ALL_LIST_ELEMENTS_RO(successors, node, entry)) + entry->flags |= EIGRP_ROUTE_DESCRIPTOR_INTABLE_FLAG; + + list_delete(&successors); + } else { + eigrp_zebra_route_delete(eigrp, prefix->destination); + for (ALL_LIST_ELEMENTS_RO(prefix->entries, node, entry)) + entry->flags &= ~EIGRP_ROUTE_DESCRIPTOR_INTABLE_FLAG; + } +} + +void eigrp_topology_neighbor_down(struct eigrp *eigrp, + struct eigrp_neighbor *nbr) +{ + struct listnode *node2, *node22; + struct eigrp_prefix_descriptor *pe; + struct eigrp_route_descriptor *entry; + struct route_node *rn; + + for (rn = route_top(eigrp->topology_table); rn; rn = route_next(rn)) { + pe = rn->info; + + if (!pe) + continue; + + for (ALL_LIST_ELEMENTS(pe->entries, node2, node22, entry)) { + struct eigrp_fsm_action_message msg; + + if (entry->adv_router != nbr) + continue; + + memset(&msg, 0, sizeof(msg)); + msg.metrics.delay = EIGRP_MAX_METRIC; + msg.packet_type = EIGRP_OPC_UPDATE; + msg.eigrp = eigrp; + msg.data_type = EIGRP_INT; + msg.adv_router = nbr; + msg.entry = entry; + msg.prefix = pe; + eigrp_fsm_event(&msg); + } + } + + eigrp_query_send_all(eigrp); + eigrp_update_send_all(eigrp, nbr->ei); +} + +void eigrp_update_topology_table_prefix(struct eigrp *eigrp, + struct route_table *table, + struct eigrp_prefix_descriptor *prefix) +{ + struct listnode *node1, *node2; + + struct eigrp_route_descriptor *entry; + for (ALL_LIST_ELEMENTS(prefix->entries, node1, node2, entry)) { + if (entry->distance == EIGRP_MAX_METRIC) { + eigrp_route_descriptor_delete(eigrp, prefix, entry); + } + } + if (prefix->distance == EIGRP_MAX_METRIC + && prefix->nt != EIGRP_TOPOLOGY_TYPE_CONNECTED) { + eigrp_prefix_descriptor_delete(eigrp, table, prefix); + } +} diff --git a/eigrpd/eigrp_topology.h b/eigrpd/eigrp_topology.h new file mode 100644 index 0000000..426c290 --- /dev/null +++ b/eigrpd/eigrp_topology.h @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Topology Table. + * Copyright (C) 2013-2016 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * Frantisek Gazo + * Tomas Hvorkovy + * Martin Kontsek + * Lukas Koribsky + */ + +#ifndef _ZEBRA_EIGRP_TOPOLOGY_H +#define _ZEBRA_EIGRP_TOPOLOGY_H + +#include "memory.h" + +DECLARE_MTYPE(EIGRP_PREFIX_DESCRIPTOR); + +/* EIGRP Topology table related functions. */ +extern struct route_table *eigrp_topology_new(void); +extern void eigrp_topology_init(struct route_table *table); +extern struct eigrp_prefix_descriptor *eigrp_prefix_descriptor_new(void); +extern struct eigrp_route_descriptor *eigrp_route_descriptor_new(void); +extern void eigrp_topology_free(struct eigrp *eigrp, struct route_table *table); +extern void eigrp_prefix_descriptor_add(struct route_table *table, + struct eigrp_prefix_descriptor *pe); +extern void eigrp_route_descriptor_add(struct eigrp *eigrp, + struct eigrp_prefix_descriptor *pe, + struct eigrp_route_descriptor *ne); +extern void eigrp_prefix_descriptor_delete(struct eigrp *eigrp, + struct route_table *table, + struct eigrp_prefix_descriptor *pe); +extern void eigrp_route_descriptor_delete(struct eigrp *eigrp, + struct eigrp_prefix_descriptor *pe, + struct eigrp_route_descriptor *ne); +extern void eigrp_topology_delete_all(struct eigrp *eigrp, + struct route_table *table); +extern struct eigrp_prefix_descriptor * +eigrp_topology_table_lookup_ipv4(struct route_table *table, struct prefix *p); +extern struct list * +eigrp_topology_get_successor(struct eigrp_prefix_descriptor *pe); +extern struct list * +eigrp_topology_get_successor_max(struct eigrp_prefix_descriptor *pe, + unsigned int maxpaths); +extern struct eigrp_route_descriptor * +eigrp_route_descriptor_lookup(struct list *entries, + struct eigrp_neighbor *neigh); +extern struct list *eigrp_neighbor_prefixes_lookup(struct eigrp *eigrp, + struct eigrp_neighbor *n); +extern void eigrp_topology_update_all_node_flags(struct eigrp *eigrp); +extern void +eigrp_topology_update_node_flags(struct eigrp *eigrp, + struct eigrp_prefix_descriptor *pe); +extern enum metric_change +eigrp_topology_update_distance(struct eigrp_fsm_action_message *msg); +extern void eigrp_update_routing_table(struct eigrp *eigrp, + struct eigrp_prefix_descriptor *pe); +extern void eigrp_topology_neighbor_down(struct eigrp *eigrp, + struct eigrp_neighbor *neigh); +extern void +eigrp_update_topology_table_prefix(struct eigrp *eigrp, + struct route_table *table, + struct eigrp_prefix_descriptor *pe); + +#endif diff --git a/eigrpd/eigrp_types.h b/eigrpd/eigrp_types.h new file mode 100644 index 0000000..f3ff99d --- /dev/null +++ b/eigrpd/eigrp_types.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Definition of Data Types + * Copyright (C) 2018 + * Authors: + * Donnie Savage + */ + +#ifndef _ZEBRA_EIGRP_TYPES_H_ +#define _ZEBRA_EIGRP_TYPES_H_ + +typedef uint64_t eigrp_bandwidth_t; +typedef uint64_t eigrp_delay_t; +typedef uint64_t eigrp_metric_t; +typedef uint32_t eigrp_scaled_t; + +typedef uint32_t eigrp_system_metric_t; +typedef uint32_t eigrp_system_delay_t; +typedef uint32_t eigrp_system_bandwidth_t; + +#endif /* _ZEBRA_EIGRP_TYPES_H_ */ diff --git a/eigrpd/eigrp_update.c b/eigrpd/eigrp_update.c new file mode 100644 index 0000000..74f573d --- /dev/null +++ b/eigrpd/eigrp_update.c @@ -0,0 +1,1038 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Sending and Receiving EIGRP Update Packets. + * Copyright (C) 2013-2016 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * Frantisek Gazo + * Tomas Hvorkovy + * Martin Kontsek + * Lukas Koribsky + */ + +#include <zebra.h> + +#include "frrevent.h" +#include "memory.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "sockunion.h" +#include "stream.h" +#include "log.h" +#include "sockopt.h" +#include "checksum.h" +#include "md5.h" +#include "vty.h" +#include "plist.h" +#include "plist_int.h" +#include "routemap.h" +#include "vty.h" + +#include "eigrpd/eigrp_types.h" +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_interface.h" +#include "eigrpd/eigrp_neighbor.h" +#include "eigrpd/eigrp_packet.h" +#include "eigrpd/eigrp_zebra.h" +#include "eigrpd/eigrp_vty.h" +#include "eigrpd/eigrp_dump.h" +#include "eigrpd/eigrp_macros.h" +#include "eigrpd/eigrp_topology.h" +#include "eigrpd/eigrp_fsm.h" +#include "eigrpd/eigrp_network.h" +#include "eigrpd/eigrp_metric.h" + +bool eigrp_update_prefix_apply(struct eigrp *eigrp, struct eigrp_interface *ei, + int in, struct prefix *prefix) +{ + struct access_list *alist; + struct prefix_list *plist; + + alist = eigrp->list[in]; + if (alist && access_list_apply(alist, prefix) == FILTER_DENY) + return true; + + plist = eigrp->prefix[in]; + if (plist && prefix_list_apply(plist, prefix) == PREFIX_DENY) + return true; + + alist = ei->list[in]; + if (alist && access_list_apply(alist, prefix) == FILTER_DENY) + return true; + + plist = ei->prefix[in]; + if (plist && prefix_list_apply(plist, prefix) == PREFIX_DENY) + return true; + + return false; +} + +/** + * @fn remove_received_prefix_gr + * + * @param[in] nbr_prefixes List of neighbor prefixes + * @param[in] recv_prefix Prefix which needs to be removed from + * list + * + * @return void + * + * @par + * Function is used for removing received prefix + * from list of neighbor prefixes + */ +static void +remove_received_prefix_gr(struct list *nbr_prefixes, + struct eigrp_prefix_descriptor *recv_prefix) +{ + struct listnode *node1, *node11; + struct eigrp_prefix_descriptor *prefix = NULL; + + /* iterate over all prefixes in list */ + for (ALL_LIST_ELEMENTS(nbr_prefixes, node1, node11, prefix)) { + /* remove prefix from list if found */ + if (prefix == recv_prefix) { + listnode_delete(nbr_prefixes, prefix); + } + } +} + +/** + * @fn eigrp_update_receive_GR_ask + * + * @param[in] eigrp EIGRP process + * @param[in] nbr Neighbor update of who we + * received + * @param[in] nbr_prefixes Prefixes which weren't advertised + * + * @return void + * + * @par + * Function is used for notifying FSM about prefixes which + * weren't advertised by neighbor: + * We will send message to FSM with prefix delay set to infinity. + */ +static void eigrp_update_receive_GR_ask(struct eigrp *eigrp, + struct eigrp_neighbor *nbr, + struct list *nbr_prefixes) +{ + struct listnode *node1; + struct eigrp_prefix_descriptor *prefix; + struct eigrp_fsm_action_message fsm_msg; + + /* iterate over all prefixes which weren't advertised by neighbor */ + for (ALL_LIST_ELEMENTS_RO(nbr_prefixes, node1, prefix)) { + zlog_debug("GR receive: Neighbor not advertised %pFX", + prefix->destination); + + fsm_msg.metrics = prefix->reported_metric; + /* set delay to MAX */ + fsm_msg.metrics.delay = EIGRP_MAX_METRIC; + + struct eigrp_route_descriptor *entry = + eigrp_route_descriptor_lookup(prefix->entries, nbr); + + fsm_msg.packet_type = EIGRP_OPC_UPDATE; + fsm_msg.eigrp = eigrp; + fsm_msg.data_type = EIGRP_INT; + fsm_msg.adv_router = nbr; + fsm_msg.entry = entry; + fsm_msg.prefix = prefix; + + /* send message to FSM */ + eigrp_fsm_event(&fsm_msg); + } +} + +/* + * EIGRP UPDATE read function + */ +void eigrp_update_receive(struct eigrp *eigrp, struct ip *iph, + struct eigrp_header *eigrph, struct stream *s, + struct eigrp_interface *ei, int size) +{ + struct eigrp_neighbor *nbr; + struct TLV_IPv4_Internal_type *tlv; + struct eigrp_prefix_descriptor *pe; + struct eigrp_route_descriptor *ne; + uint32_t flags; + uint16_t type; + uint16_t length; + uint8_t same; + struct prefix dest_addr; + uint8_t graceful_restart; + uint8_t graceful_restart_final; + struct list *nbr_prefixes = NULL; + + /* increment statistics. */ + ei->update_in++; + + /* get neighbor struct */ + nbr = eigrp_nbr_get(ei, eigrph, iph); + + /* neighbor must be valid, eigrp_nbr_get creates if none existed */ + assert(nbr); + + flags = ntohl(eigrph->flags); + + if (flags & EIGRP_CR_FLAG) { + return; + } + + same = 0; + graceful_restart = 0; + graceful_restart_final = 0; + if ((nbr->recv_sequence_number) == (ntohl(eigrph->sequence))) + same = 1; + + nbr->recv_sequence_number = ntohl(eigrph->sequence); + if (IS_DEBUG_EIGRP_PACKET(0, RECV)) + zlog_debug( + "Processing Update size[%u] int(%s) nbr(%pI4) seq [%u] flags [%0x]", + size, + ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id), + &nbr->src, nbr->recv_sequence_number, flags); + + + if ((flags == (EIGRP_INIT_FLAG + EIGRP_RS_FLAG + EIGRP_EOT_FLAG)) + && (!same)) { + /* Graceful restart Update received with all routes */ + + zlog_info("Neighbor %pI4 (%s) is resync: peer graceful-restart", + &nbr->src, + ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id)); + + /* get all prefixes from neighbor from topology table */ + nbr_prefixes = eigrp_neighbor_prefixes_lookup(eigrp, nbr); + graceful_restart = 1; + graceful_restart_final = 1; + } else if ((flags == (EIGRP_INIT_FLAG + EIGRP_RS_FLAG)) && (!same)) { + /* Graceful restart Update received, routes also in next packet + */ + + zlog_info("Neighbor %pI4 (%s) is resync: peer graceful-restart", + &nbr->src, + ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id)); + + /* get all prefixes from neighbor from topology table */ + nbr_prefixes = eigrp_neighbor_prefixes_lookup(eigrp, nbr); + /* save prefixes to neighbor for later use */ + nbr->nbr_gr_prefixes = nbr_prefixes; + graceful_restart = 1; + graceful_restart_final = 0; + } else if ((flags == (EIGRP_EOT_FLAG)) && (!same)) { + /* If there was INIT+RS Update packet before, + * consider this as GR EOT */ + + if (nbr->nbr_gr_prefixes != NULL) { + /* this is final packet of GR */ + nbr_prefixes = nbr->nbr_gr_prefixes; + nbr->nbr_gr_prefixes = NULL; + + graceful_restart = 1; + graceful_restart_final = 1; + } + + } else if ((flags == (0)) && (!same)) { + /* If there was INIT+RS Update packet before, + * consider this as GR not final packet */ + + if (nbr->nbr_gr_prefixes != NULL) { + /* this is GR not final route packet */ + nbr_prefixes = nbr->nbr_gr_prefixes; + + graceful_restart = 1; + graceful_restart_final = 0; + } + + } else if ((flags & EIGRP_INIT_FLAG) + && (!same)) { /* When in pending state, send INIT update only + if it wasn't + already sent before (only if init_sequence + is 0) */ + if ((nbr->state == EIGRP_NEIGHBOR_PENDING) + && (nbr->init_sequence_number == 0)) + eigrp_update_send_init(nbr); + + if (nbr->state == EIGRP_NEIGHBOR_UP) { + eigrp_nbr_state_set(nbr, EIGRP_NEIGHBOR_DOWN); + eigrp_topology_neighbor_down(nbr->ei->eigrp, nbr); + nbr->recv_sequence_number = ntohl(eigrph->sequence); + zlog_info("Neighbor %pI4 (%s) is down: peer restarted", + &nbr->src, + ifindex2ifname(nbr->ei->ifp->ifindex, + eigrp->vrf_id)); + eigrp_nbr_state_set(nbr, EIGRP_NEIGHBOR_PENDING); + zlog_info( + "Neighbor %pI4 (%s) is pending: new adjacency", + &nbr->src, + ifindex2ifname(nbr->ei->ifp->ifindex, + eigrp->vrf_id)); + eigrp_update_send_init(nbr); + } + } + + /*If there is topology information*/ + while (s->endp > s->getp) { + type = stream_getw(s); + switch (type) { + case EIGRP_TLV_IPv4_INT: + stream_set_getp(s, s->getp - sizeof(uint16_t)); + + tlv = eigrp_read_ipv4_tlv(s); + + /*searching if destination exists */ + dest_addr.family = AF_INET; + dest_addr.u.prefix4 = tlv->destination; + dest_addr.prefixlen = tlv->prefix_length; + struct eigrp_prefix_descriptor *dest = + eigrp_topology_table_lookup_ipv4( + eigrp->topology_table, &dest_addr); + + /*if exists it comes to DUAL*/ + if (dest != NULL) { + /* remove received prefix from neighbor prefix + * list if in GR */ + if (graceful_restart) + remove_received_prefix_gr(nbr_prefixes, + dest); + + struct eigrp_fsm_action_message msg; + struct eigrp_route_descriptor *entry = + eigrp_route_descriptor_lookup( + dest->entries, nbr); + + msg.packet_type = EIGRP_OPC_UPDATE; + msg.eigrp = eigrp; + msg.data_type = EIGRP_INT; + msg.adv_router = nbr; + msg.metrics = tlv->metric; + msg.entry = entry; + msg.prefix = dest; + eigrp_fsm_event(&msg); + } else { + /*Here comes topology information save*/ + pe = eigrp_prefix_descriptor_new(); + pe->serno = eigrp->serno; + pe->destination = + (struct prefix *)prefix_ipv4_new(); + prefix_copy(pe->destination, &dest_addr); + pe->af = AF_INET; + pe->state = EIGRP_FSM_STATE_PASSIVE; + pe->nt = EIGRP_TOPOLOGY_TYPE_REMOTE; + + ne = eigrp_route_descriptor_new(); + ne->ei = ei; + ne->adv_router = nbr; + ne->reported_metric = tlv->metric; + ne->reported_distance = eigrp_calculate_metrics( + eigrp, tlv->metric); + /* + * Filtering + */ + if (eigrp_update_prefix_apply(eigrp, ei, + EIGRP_FILTER_IN, + &dest_addr)) + ne->reported_metric.delay = + EIGRP_MAX_METRIC; + + ne->distance = eigrp_calculate_total_metrics( + eigrp, ne); + + pe->fdistance = pe->distance = pe->rdistance = + ne->distance; + ne->prefix = pe; + ne->flags = + EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG; + + eigrp_prefix_descriptor_add( + eigrp->topology_table, pe); + eigrp_route_descriptor_add(eigrp, pe, ne); + pe->distance = pe->fdistance = pe->rdistance = + ne->distance; + pe->reported_metric = ne->total_metric; + eigrp_topology_update_node_flags(eigrp, pe); + + pe->req_action |= EIGRP_FSM_NEED_UPDATE; + listnode_add( + eigrp->topology_changes_internalIPV4, + pe); + } + eigrp_IPv4_InternalTLV_free(tlv); + break; + + case EIGRP_TLV_IPv4_EXT: + /* DVS: processing of external routes needs packet and fsm work. + * for now, lets just not creash the box + */ + default: + length = stream_getw(s); + // -2 for type, -2 for len + for (length -= 4; length; length--) { + (void)stream_getc(s); + } + } + } + + /* ask about prefixes not present in GR update, + * if this is final GR packet */ + if (graceful_restart_final) { + eigrp_update_receive_GR_ask(eigrp, nbr, nbr_prefixes); + } + + /* + * We don't need to send separate Ack for INIT Update. INIT will be + * acked in EOT Update. + */ + if ((nbr->state == EIGRP_NEIGHBOR_UP) && !(flags == EIGRP_INIT_FLAG)) { + eigrp_hello_send_ack(nbr); + } + + eigrp_query_send_all(eigrp); + eigrp_update_send_all(eigrp, ei); + + if (nbr_prefixes) + list_delete(&nbr_prefixes); +} + +/*send EIGRP Update packet*/ +void eigrp_update_send_init(struct eigrp_neighbor *nbr) +{ + struct eigrp_packet *ep; + uint16_t length = EIGRP_HEADER_LEN; + + ep = eigrp_packet_new(EIGRP_PACKET_MTU(nbr->ei->ifp->mtu), nbr); + + /* Prepare EIGRP INIT UPDATE header */ + if (IS_DEBUG_EIGRP_PACKET(0, RECV)) + zlog_debug("Enqueuing Update Init Seq [%u] Ack [%u]", + nbr->ei->eigrp->sequence_number, + nbr->recv_sequence_number); + + eigrp_packet_header_init( + EIGRP_OPC_UPDATE, nbr->ei->eigrp, ep->s, EIGRP_INIT_FLAG, + nbr->ei->eigrp->sequence_number, nbr->recv_sequence_number); + + // encode Authentication TLV, if needed + if ((nbr->ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) + && (nbr->ei->params.auth_keychain != NULL)) { + length += eigrp_add_authTLV_MD5_to_stream(ep->s, nbr->ei); + eigrp_make_md5_digest(nbr->ei, ep->s, + EIGRP_AUTH_UPDATE_INIT_FLAG); + } + + /* EIGRP Checksum */ + eigrp_packet_checksum(nbr->ei, ep->s, length); + + ep->length = length; + ep->dst.s_addr = nbr->src.s_addr; + + /*This ack number we await from neighbor*/ + nbr->init_sequence_number = nbr->ei->eigrp->sequence_number; + ep->sequence_number = nbr->ei->eigrp->sequence_number; + if (IS_DEBUG_EIGRP_PACKET(0, RECV)) + zlog_debug( + "Enqueuing Update Init Len [%u] Seq [%u] Dest [%pI4]", + ep->length, ep->sequence_number, &ep->dst); + + /*Put packet to retransmission queue*/ + eigrp_fifo_push(nbr->retrans_queue, ep); + + if (nbr->retrans_queue->count == 1) { + eigrp_send_packet_reliably(nbr); + } +} + +static void eigrp_update_place_on_nbr_queue(struct eigrp_neighbor *nbr, + struct eigrp_packet *ep, + uint32_t seq_no, int length) +{ + if ((nbr->ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) + && (nbr->ei->params.auth_keychain != NULL)) { + eigrp_make_md5_digest(nbr->ei, ep->s, EIGRP_AUTH_UPDATE_FLAG); + } + + /* EIGRP Checksum */ + eigrp_packet_checksum(nbr->ei, ep->s, length); + + ep->length = length; + ep->dst.s_addr = nbr->src.s_addr; + + /*This ack number we await from neighbor*/ + ep->sequence_number = seq_no; + + if (IS_DEBUG_EIGRP_PACKET(0, RECV)) + zlog_debug( + "Enqueuing Update Init Len [%u] Seq [%u] Dest [%pI4]", + ep->length, ep->sequence_number, &ep->dst); + + /*Put packet to retransmission queue*/ + eigrp_fifo_push(nbr->retrans_queue, ep); + + if (nbr->retrans_queue->count == 1) + eigrp_send_packet_reliably(nbr); +} + +static void eigrp_update_send_to_all_nbrs(struct eigrp_interface *ei, + struct eigrp_packet *ep) +{ + struct listnode *node, *nnode; + struct eigrp_neighbor *nbr; + bool packet_sent = false; + + for (ALL_LIST_ELEMENTS(ei->nbrs, node, nnode, nbr)) { + struct eigrp_packet *ep_dup; + + if (nbr->state != EIGRP_NEIGHBOR_UP) + continue; + + if (packet_sent) + ep_dup = eigrp_packet_duplicate(ep, NULL); + else + ep_dup = ep; + + ep_dup->nbr = nbr; + packet_sent = true; + /*Put packet to retransmission queue*/ + eigrp_fifo_push(nbr->retrans_queue, ep_dup); + + if (nbr->retrans_queue->count == 1) { + eigrp_send_packet_reliably(nbr); + } + } + + if (!packet_sent) + eigrp_packet_free(ep); +} + +void eigrp_update_send_EOT(struct eigrp_neighbor *nbr) +{ + struct eigrp_packet *ep; + uint16_t length = EIGRP_HEADER_LEN; + struct eigrp_route_descriptor *te; + struct eigrp_prefix_descriptor *pe; + struct listnode *node2, *nnode2; + struct eigrp_interface *ei = nbr->ei; + struct eigrp *eigrp = ei->eigrp; + struct prefix *dest_addr; + uint32_t seq_no = eigrp->sequence_number; + uint16_t eigrp_mtu = EIGRP_PACKET_MTU(ei->ifp->mtu); + struct route_node *rn; + + ep = eigrp_packet_new(eigrp_mtu, nbr); + + /* Prepare EIGRP EOT UPDATE header */ + eigrp_packet_header_init(EIGRP_OPC_UPDATE, eigrp, ep->s, EIGRP_EOT_FLAG, + seq_no, nbr->recv_sequence_number); + + // encode Authentication TLV, if needed + if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) + && (ei->params.auth_keychain != NULL)) { + length += eigrp_add_authTLV_MD5_to_stream(ep->s, ei); + } + + for (rn = route_top(eigrp->topology_table); rn; rn = route_next(rn)) { + if (!rn->info) + continue; + + pe = rn->info; + for (ALL_LIST_ELEMENTS(pe->entries, node2, nnode2, te)) { + if (eigrp_nbr_split_horizon_check(te, ei)) + continue; + + if ((length + EIGRP_TLV_MAX_IPV4_BYTE) > eigrp_mtu) { + eigrp_update_place_on_nbr_queue(nbr, ep, seq_no, + length); + seq_no++; + + length = EIGRP_HEADER_LEN; + ep = eigrp_packet_new(eigrp_mtu, nbr); + eigrp_packet_header_init( + EIGRP_OPC_UPDATE, nbr->ei->eigrp, ep->s, + EIGRP_EOT_FLAG, seq_no, + nbr->recv_sequence_number); + + if ((ei->params.auth_type + == EIGRP_AUTH_TYPE_MD5) + && (ei->params.auth_keychain != NULL)) { + length += + eigrp_add_authTLV_MD5_to_stream( + ep->s, ei); + } + } + /* Get destination address from prefix */ + dest_addr = pe->destination; + + /* Check if any list fits */ + if (eigrp_update_prefix_apply( + eigrp, ei, EIGRP_FILTER_OUT, dest_addr)) + continue; + else { + length += eigrp_add_internalTLV_to_stream(ep->s, + pe); + } + } + } + + eigrp_update_place_on_nbr_queue(nbr, ep, seq_no, length); + eigrp->sequence_number = seq_no++; +} + +void eigrp_update_send(struct eigrp_interface *ei) +{ + struct eigrp_packet *ep; + struct listnode *node, *nnode; + struct eigrp_prefix_descriptor *pe; + uint8_t has_tlv; + struct eigrp *eigrp = ei->eigrp; + struct prefix *dest_addr; + uint32_t seq_no = eigrp->sequence_number; + uint16_t eigrp_mtu = EIGRP_PACKET_MTU(ei->ifp->mtu); + + if (ei->nbrs->count == 0) + return; + + uint16_t length = EIGRP_HEADER_LEN; + + ep = eigrp_packet_new(eigrp_mtu, NULL); + + /* Prepare EIGRP INIT UPDATE header */ + eigrp_packet_header_init(EIGRP_OPC_UPDATE, eigrp, ep->s, 0, seq_no, 0); + + // encode Authentication TLV, if needed + if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) + && (ei->params.auth_keychain != NULL)) { + length += eigrp_add_authTLV_MD5_to_stream(ep->s, ei); + } + + has_tlv = 0; + for (ALL_LIST_ELEMENTS(ei->eigrp->topology_changes_internalIPV4, node, + nnode, pe)) { + struct eigrp_route_descriptor *ne; + + if (!(pe->req_action & EIGRP_FSM_NEED_UPDATE)) + continue; + + ne = listnode_head(pe->entries); + if (eigrp_nbr_split_horizon_check(ne, ei)) + continue; + + if ((length + EIGRP_TLV_MAX_IPV4_BYTE) > eigrp_mtu) { + if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) + && (ei->params.auth_keychain != NULL)) { + eigrp_make_md5_digest(ei, ep->s, + EIGRP_AUTH_UPDATE_FLAG); + } + + eigrp_packet_checksum(ei, ep->s, length); + ep->length = length; + + ep->dst.s_addr = htonl(EIGRP_MULTICAST_ADDRESS); + + ep->sequence_number = seq_no; + seq_no++; + eigrp_update_send_to_all_nbrs(ei, ep); + + length = EIGRP_HEADER_LEN; + ep = eigrp_packet_new(eigrp_mtu, NULL); + eigrp_packet_header_init(EIGRP_OPC_UPDATE, eigrp, ep->s, + 0, seq_no, 0); + if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) + && (ei->params.auth_keychain != NULL)) { + length += eigrp_add_authTLV_MD5_to_stream(ep->s, + ei); + } + has_tlv = 0; + } + /* Get destination address from prefix */ + dest_addr = pe->destination; + + if (eigrp_update_prefix_apply(eigrp, ei, EIGRP_FILTER_OUT, + dest_addr)) { + // pe->reported_metric.delay = EIGRP_MAX_METRIC; + continue; + } else { + length += eigrp_add_internalTLV_to_stream(ep->s, pe); + has_tlv = 1; + } + } + + if (!has_tlv) { + eigrp_packet_free(ep); + return; + } + + if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) + && (ei->params.auth_keychain != NULL)) { + eigrp_make_md5_digest(ei, ep->s, EIGRP_AUTH_UPDATE_FLAG); + } + + /* EIGRP Checksum */ + eigrp_packet_checksum(ei, ep->s, length); + ep->length = length; + + ep->dst.s_addr = htonl(EIGRP_MULTICAST_ADDRESS); + + /*This ack number we await from neighbor*/ + ep->sequence_number = eigrp->sequence_number; + + if (IS_DEBUG_EIGRP_PACKET(0, RECV)) + zlog_debug("Enqueuing Update length[%u] Seq [%u]", length, + ep->sequence_number); + + eigrp_update_send_to_all_nbrs(ei, ep); + ei->eigrp->sequence_number = seq_no++; +} + +void eigrp_update_send_all(struct eigrp *eigrp, + struct eigrp_interface *exception) +{ + struct eigrp_interface *iface; + struct listnode *node, *node2, *nnode2; + struct eigrp_prefix_descriptor *pe; + + for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, iface)) { + if (iface != exception) { + eigrp_update_send(iface); + } + } + + for (ALL_LIST_ELEMENTS(eigrp->topology_changes_internalIPV4, node2, + nnode2, pe)) { + if (pe->req_action & EIGRP_FSM_NEED_UPDATE) { + pe->req_action &= ~EIGRP_FSM_NEED_UPDATE; + listnode_delete(eigrp->topology_changes_internalIPV4, + pe); + } + } +} + +/** + * @fn eigrp_update_send_GR_part + * + * @param[in] nbr contains neighbor who would receive + * Graceful + * restart + * + * @return void + * + * @par + * Function used for sending Graceful restart Update packet + * and if there are multiple chunks, send only one of them. + * It is called from thread. Do not call it directly. + * + * Uses nbr_gr_packet_type from neighbor. + */ +static void eigrp_update_send_GR_part(struct eigrp_neighbor *nbr) +{ + struct eigrp_packet *ep; + uint16_t length = EIGRP_HEADER_LEN; + struct eigrp_prefix_descriptor *pe; + struct prefix *dest_addr; + struct eigrp_interface *ei = nbr->ei; + struct eigrp *eigrp = ei->eigrp; + struct list *prefixes; + uint32_t flags; + unsigned int send_prefixes; + struct route_node *rn; + + /* get prefixes to send to neighbor */ + prefixes = nbr->nbr_gr_prefixes_send; + + send_prefixes = 0; + + /* if there already were last packet chunk, we won't continue */ + if (nbr->nbr_gr_packet_type == EIGRP_PACKET_PART_LAST) + return; + + /* if this is first packet chunk, we need to decide, + * if there will be one or more chunks */ + if (nbr->nbr_gr_packet_type == EIGRP_PACKET_PART_FIRST) { + if (prefixes->count <= EIGRP_TLV_MAX_IPv4) { + /* there will be only one chunk */ + flags = EIGRP_INIT_FLAG + EIGRP_RS_FLAG + + EIGRP_EOT_FLAG; + nbr->nbr_gr_packet_type = EIGRP_PACKET_PART_LAST; + } else { + /* there will be more chunks */ + flags = EIGRP_INIT_FLAG + EIGRP_RS_FLAG; + nbr->nbr_gr_packet_type = EIGRP_PACKET_PART_NA; + } + } else { + /* this is not first chunk, and we need to decide, + * if there will be more chunks */ + if (prefixes->count <= EIGRP_TLV_MAX_IPv4) { + /* this is last chunk */ + flags = EIGRP_EOT_FLAG; + nbr->nbr_gr_packet_type = EIGRP_PACKET_PART_LAST; + } else { + /* there will be more chunks */ + flags = 0; + nbr->nbr_gr_packet_type = EIGRP_PACKET_PART_NA; + } + } + + ep = eigrp_packet_new(EIGRP_PACKET_MTU(ei->ifp->mtu), nbr); + + /* Prepare EIGRP Graceful restart UPDATE header */ + eigrp_packet_header_init(EIGRP_OPC_UPDATE, eigrp, ep->s, flags, + eigrp->sequence_number, + nbr->recv_sequence_number); + + // encode Authentication TLV, if needed + if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) + && (ei->params.auth_keychain != NULL)) { + length += eigrp_add_authTLV_MD5_to_stream(ep->s, ei); + } + + for (rn = route_top(eigrp->topology_table); rn; rn = route_next(rn)) { + if (!rn->info) + continue; + + pe = rn->info; + /* + * Filtering + */ + dest_addr = pe->destination; + + if (eigrp_update_prefix_apply(eigrp, ei, EIGRP_FILTER_OUT, + dest_addr)) { + /* do not send filtered route */ + zlog_info("Filtered prefix %pI4 won't be sent out.", + &dest_addr->u.prefix4); + } else { + /* sending route which wasn't filtered */ + length += eigrp_add_internalTLV_to_stream(ep->s, pe); + send_prefixes++; + } + + /* + * This makes no sense, Filter out then filter in??? + * Look into this more - DBS + */ + if (eigrp_update_prefix_apply(eigrp, ei, EIGRP_FILTER_IN, + dest_addr)) { + /* do not send filtered route */ + zlog_info("Filtered prefix %pI4 will be removed.", + &dest_addr->u.prefix4); + + /* prepare message for FSM */ + struct eigrp_fsm_action_message fsm_msg; + + struct eigrp_route_descriptor *entry = + eigrp_route_descriptor_lookup(pe->entries, nbr); + + fsm_msg.packet_type = EIGRP_OPC_UPDATE; + fsm_msg.eigrp = eigrp; + fsm_msg.data_type = EIGRP_INT; + fsm_msg.adv_router = nbr; + fsm_msg.metrics = pe->reported_metric; + /* Set delay to MAX */ + fsm_msg.metrics.delay = EIGRP_MAX_METRIC; + fsm_msg.entry = entry; + fsm_msg.prefix = pe; + + /* send message to FSM */ + eigrp_fsm_event(&fsm_msg); + } + + /* delete processed prefix from list */ + listnode_delete(prefixes, pe); + + /* if there are enough prefixes, send packet */ + if (send_prefixes >= EIGRP_TLV_MAX_IPv4) + break; + } + + /* compute Auth digest */ + if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) + && (ei->params.auth_keychain != NULL)) { + eigrp_make_md5_digest(ei, ep->s, EIGRP_AUTH_UPDATE_FLAG); + } + + /* EIGRP Checksum */ + eigrp_packet_checksum(ei, ep->s, length); + + ep->length = length; + ep->dst.s_addr = nbr->src.s_addr; + + /*This ack number we await from neighbor*/ + ep->sequence_number = eigrp->sequence_number; + + if (IS_DEBUG_EIGRP_PACKET(0, RECV)) + zlog_debug( + "Enqueuing Update Init Len [%u] Seq [%u] Dest [%pI4]", + ep->length, ep->sequence_number, &ep->dst); + + /*Put packet to retransmission queue*/ + eigrp_fifo_push(nbr->retrans_queue, ep); + + if (nbr->retrans_queue->count == 1) { + eigrp_send_packet_reliably(nbr); + } +} + +/** + * @fn eigrp_update_send_GR_thread + * + * @param[in] thread contains neighbor who would receive + * Graceful restart + * + * @return int always 0 + * + * @par + * Function used for sending Graceful restart Update packet + * in thread, it is prepared for multiple chunks of packet. + * + * Uses nbr_gr_packet_type and t_nbr_send_gr from neighbor. + */ +void eigrp_update_send_GR_thread(struct event *thread) +{ + struct eigrp_neighbor *nbr; + + /* get argument from thread */ + nbr = EVENT_ARG(thread); + /* remove this thread pointer */ + + /* if there is packet waiting in queue, + * schedule this thread again with small delay */ + if (nbr->retrans_queue->count > 0) { + event_add_timer_msec(master, eigrp_update_send_GR_thread, nbr, + 10, &nbr->t_nbr_send_gr); + return; + } + + /* send GR EIGRP packet chunk */ + eigrp_update_send_GR_part(nbr); + + /* if it wasn't last chunk, schedule this thread again */ + if (nbr->nbr_gr_packet_type != EIGRP_PACKET_PART_LAST) { + event_execute(master, eigrp_update_send_GR_thread, nbr, 0, NULL); + } +} + +/** + * @fn eigrp_update_send_GR + * + * @param[in] nbr Neighbor who would receive + * Graceful + * restart + * @param[in] gr_type Who executed Graceful restart + * @param[in] vty Virtual terminal for log output + * + * @return void + * + * @par + * Function used for sending Graceful restart Update packet: + * Creates Update packet with INIT, RS, EOT flags and include + * all route except those filtered + */ +void eigrp_update_send_GR(struct eigrp_neighbor *nbr, enum GR_type gr_type, + struct vty *vty) +{ + struct eigrp_prefix_descriptor *pe2; + struct list *prefixes; + struct route_node *rn; + struct eigrp_interface *ei = nbr->ei; + struct eigrp *eigrp = ei->eigrp; + + if (gr_type == EIGRP_GR_FILTER) { + /* function was called after applying filtration */ + zlog_info( + "Neighbor %pI4 (%s) is resync: route configuration changed", + &nbr->src, + ifindex2ifname(ei->ifp->ifindex, eigrp->vrf_id)); + } else if (gr_type == EIGRP_GR_MANUAL) { + /* Graceful restart was called manually */ + zlog_info("Neighbor %pI4 (%s) is resync: manually cleared", + &nbr->src, + ifindex2ifname(ei->ifp->ifindex, eigrp->vrf_id)); + + if (vty != NULL) { + vty_time_print(vty, 0); + vty_out(vty, + "Neighbor %pI4 (%s) is resync: manually cleared\n", + &nbr->src, + ifindex2ifname(ei->ifp->ifindex, + eigrp->vrf_id)); + } + } + + prefixes = list_new(); + /* add all prefixes from topology table to list */ + for (rn = route_top(eigrp->topology_table); rn; rn = route_next(rn)) { + if (!rn->info) + continue; + + pe2 = rn->info; + listnode_add(prefixes, pe2); + } + + /* save prefixes to neighbor */ + nbr->nbr_gr_prefixes_send = prefixes; + /* indicate, that this is first GR Update packet chunk */ + nbr->nbr_gr_packet_type = EIGRP_PACKET_PART_FIRST; + /* execute packet sending in thread */ + event_execute(master, eigrp_update_send_GR_thread, nbr, 0, NULL); +} + +/** + * @fn eigrp_update_send_interface_GR + * + * @param[in] ei Interface to neighbors of which + * the + * GR + * is sent + * @param[in] gr_type Who executed Graceful restart + * @param[in] vty Virtual terminal for log output + * + * @return void + * + * @par + * Function used for sending Graceful restart Update packet + * to all neighbors on specified interface. + */ +void eigrp_update_send_interface_GR(struct eigrp_interface *ei, + enum GR_type gr_type, struct vty *vty) +{ + struct listnode *node; + struct eigrp_neighbor *nbr; + + /* iterate over all neighbors on eigrp interface */ + for (ALL_LIST_ELEMENTS_RO(ei->nbrs, node, nbr)) { + /* send GR to neighbor */ + eigrp_update_send_GR(nbr, gr_type, vty); + } +} + +/** + * @fn eigrp_update_send_process_GR + * + * @param[in] eigrp EIGRP process + * @param[in] gr_type Who executed Graceful restart + * @param[in] vty Virtual terminal for log output + * + * @return void + * + * @par + * Function used for sending Graceful restart Update packet + * to all neighbors in eigrp process. + */ +void eigrp_update_send_process_GR(struct eigrp *eigrp, enum GR_type gr_type, + struct vty *vty) +{ + struct listnode *node; + struct eigrp_interface *ei; + + /* iterate over all eigrp interfaces */ + for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { + /* send GR to all neighbors on interface */ + eigrp_update_send_interface_GR(ei, gr_type, vty); + } +} diff --git a/eigrpd/eigrp_vrf.c b/eigrpd/eigrp_vrf.c new file mode 100644 index 0000000..645233e --- /dev/null +++ b/eigrpd/eigrp_vrf.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * eigrp - vrf code + * Copyright (C) 2019 Cumulus Networks, Inc. + * Donald Sharp + */ +#include <zebra.h> + +#include "vrf.h" + +#include "eigrpd/eigrp_vrf.h" + +static int eigrp_vrf_new(struct vrf *vrf) +{ + return 0; +} + +static int eigrp_vrf_enable(struct vrf *vrf) +{ + return 0; +} + +static int eigrp_vrf_disable(struct vrf *vrf) +{ + return 0; +} + +static int eigrp_vrf_delete(struct vrf *vrf) +{ + return 0; +} + +void eigrp_vrf_init(void) +{ + vrf_init(eigrp_vrf_new, eigrp_vrf_enable, eigrp_vrf_disable, + eigrp_vrf_delete); +} diff --git a/eigrpd/eigrp_vrf.h b/eigrpd/eigrp_vrf.h new file mode 100644 index 0000000..146d864 --- /dev/null +++ b/eigrpd/eigrp_vrf.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * eigrp - vrf code + * Copyright (C) 2019 Cumulus Networks, Inc. + * Donald Sharp + */ +#ifndef __EIGRP_VRF_H__ + +extern void eigrp_vrf_init(void); +#endif diff --git a/eigrpd/eigrp_vty.c b/eigrpd/eigrp_vty.c new file mode 100644 index 0000000..88510e7 --- /dev/null +++ b/eigrpd/eigrp_vty.c @@ -0,0 +1,587 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP VTY Interface. + * Copyright (C) 2013-2016 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * Frantisek Gazo + * Tomas Hvorkovy + * Martin Kontsek + * Lukas Koribsky + */ + +#include <zebra.h> + +#include "memory.h" +#include "frrevent.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "command.h" +#include "plist.h" +#include "log.h" +#include "zclient.h" +#include "keychain.h" +#include "linklist.h" +#include "distribute.h" + +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_interface.h" +#include "eigrpd/eigrp_neighbor.h" +#include "eigrpd/eigrp_packet.h" +#include "eigrpd/eigrp_zebra.h" +#include "eigrpd/eigrp_vty.h" +#include "eigrpd/eigrp_network.h" +#include "eigrpd/eigrp_dump.h" +#include "eigrpd/eigrp_const.h" + +#include "eigrpd/eigrp_vty_clippy.c" + +static void eigrp_vty_display_prefix_entry(struct vty *vty, struct eigrp *eigrp, + struct eigrp_prefix_descriptor *pe, + bool all) +{ + bool first = true; + struct eigrp_route_descriptor *te; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(pe->entries, node, te)) { + if (all + || (((te->flags & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG) + == EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG) + || ((te->flags & EIGRP_ROUTE_DESCRIPTOR_FSUCCESSOR_FLAG) + == EIGRP_ROUTE_DESCRIPTOR_FSUCCESSOR_FLAG))) { + show_ip_eigrp_route_descriptor(vty, eigrp, te, &first); + first = false; + } + } +} + +static struct eigrp *eigrp_vty_get_eigrp(struct vty *vty, const char *vrf_name) +{ + struct vrf *vrf; + + if (vrf_name) + vrf = vrf_lookup_by_name(vrf_name); + else + vrf = vrf_lookup_by_id(VRF_DEFAULT); + + if (!vrf) { + vty_out(vty, "VRF %s specified does not exist", + vrf_name ? vrf_name : VRF_DEFAULT_NAME); + return NULL; + } + + return eigrp_lookup(vrf->vrf_id); +} + +static void eigrp_topology_helper(struct vty *vty, struct eigrp *eigrp, + const char *all) +{ + struct eigrp_prefix_descriptor *tn; + struct route_node *rn; + + show_ip_eigrp_topology_header(vty, eigrp); + + for (rn = route_top(eigrp->topology_table); rn; rn = route_next(rn)) { + if (!rn->info) + continue; + + tn = rn->info; + eigrp_vty_display_prefix_entry(vty, eigrp, tn, + all ? true : false); + } +} + +DEFPY (show_ip_eigrp_topology_all, + show_ip_eigrp_topology_all_cmd, + "show ip eigrp [vrf NAME] topology [all-links$all]", + SHOW_STR + IP_STR + "IP-EIGRP show commands\n" + VRF_CMD_HELP_STR + "IP-EIGRP topology\n" + "Show all links in topology table\n") +{ + struct eigrp *eigrp; + + if (vrf && strncmp(vrf, "all", sizeof("all")) == 0) { + struct vrf *v; + + RB_FOREACH (v, vrf_name_head, &vrfs_by_name) { + eigrp = eigrp_lookup(v->vrf_id); + if (!eigrp) + continue; + + vty_out(vty, "VRF %s:\n", v->name); + + eigrp_topology_helper(vty, eigrp, all); + } + } else { + eigrp = eigrp_vty_get_eigrp(vty, vrf); + if (eigrp == NULL) { + vty_out(vty, " EIGRP Routing Process not enabled\n"); + return CMD_SUCCESS; + } + + eigrp_topology_helper(vty, eigrp, all); + } + + return CMD_SUCCESS; +} + +DEFPY (show_ip_eigrp_topology, + show_ip_eigrp_topology_cmd, + "show ip eigrp [vrf NAME] topology <A.B.C.D$address|A.B.C.D/M$prefix>", + SHOW_STR + IP_STR + "IP-EIGRP show commands\n" + VRF_CMD_HELP_STR + "IP-EIGRP topology\n" + "For a specific address\n" + "For a specific prefix\n") +{ + struct eigrp *eigrp; + struct eigrp_prefix_descriptor *tn; + struct route_node *rn; + struct prefix cmp; + + if (vrf && strncmp(vrf, "all", sizeof("all")) == 0) { + vty_out(vty, "Specifying vrf `all` for a particular address/prefix makes no sense\n"); + return CMD_SUCCESS; + } + + eigrp = eigrp_vty_get_eigrp(vty, vrf); + if (eigrp == NULL) { + vty_out(vty, " EIGRP Routing Process not enabled\n"); + return CMD_SUCCESS; + } + + show_ip_eigrp_topology_header(vty, eigrp); + + if (address_str) + prefix_str = address_str; + + if (str2prefix(prefix_str, &cmp) < 0) { + vty_out(vty, "%% Malformed address\n"); + return CMD_WARNING; + } + + rn = route_node_match(eigrp->topology_table, &cmp); + if (!rn) { + vty_out(vty, "%% Network not in table\n"); + return CMD_WARNING; + } + + if (!rn->info) { + vty_out(vty, "%% Network not in table\n"); + route_unlock_node(rn); + return CMD_WARNING; + } + + tn = rn->info; + eigrp_vty_display_prefix_entry(vty, eigrp, tn, argc == 5); + + route_unlock_node(rn); + return CMD_SUCCESS; +} + +static void eigrp_interface_helper(struct vty *vty, struct eigrp *eigrp, + const char *ifname, const char *detail) +{ + struct eigrp_interface *ei; + struct listnode *node; + + if (!ifname) + show_ip_eigrp_interface_header(vty, eigrp); + + for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { + if (!ifname || strcmp(ei->ifp->name, ifname) == 0) { + show_ip_eigrp_interface_sub(vty, eigrp, ei); + if (detail) + show_ip_eigrp_interface_detail(vty, eigrp, ei); + } + } +} + +DEFPY (show_ip_eigrp_interfaces, + show_ip_eigrp_interfaces_cmd, + "show ip eigrp [vrf NAME] interfaces [IFNAME] [detail]$detail", + SHOW_STR + IP_STR + "IP-EIGRP show commands\n" + VRF_CMD_HELP_STR + "IP-EIGRP interfaces\n" + "Interface name to look at\n" + "Detailed information\n") +{ + struct eigrp *eigrp; + + if (vrf && strncmp(vrf, "all", sizeof("all")) == 0) { + struct vrf *v; + + RB_FOREACH (v, vrf_name_head, &vrfs_by_name) { + eigrp = eigrp_lookup(v->vrf_id); + if (!eigrp) + continue; + + vty_out(vty, "VRF %s:\n", v->name); + + eigrp_interface_helper(vty, eigrp, ifname, detail); + } + } else { + eigrp = eigrp_vty_get_eigrp(vty, vrf); + if (eigrp == NULL) { + vty_out(vty, "EIGRP Routing Process not enabled\n"); + return CMD_SUCCESS; + } + + eigrp_interface_helper(vty, eigrp, ifname, detail); + } + + + return CMD_SUCCESS; +} + +static void eigrp_neighbors_helper(struct vty *vty, struct eigrp *eigrp, + const char *ifname, const char *detail) +{ + struct eigrp_interface *ei; + struct listnode *node, *node2, *nnode2; + struct eigrp_neighbor *nbr; + + show_ip_eigrp_neighbor_header(vty, eigrp); + + for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { + if (!ifname || strcmp(ei->ifp->name, ifname) == 0) { + for (ALL_LIST_ELEMENTS(ei->nbrs, node2, nnode2, nbr)) { + if (detail || (nbr->state == EIGRP_NEIGHBOR_UP)) + show_ip_eigrp_neighbor_sub(vty, nbr, + !!detail); + } + } + } +} + +DEFPY (show_ip_eigrp_neighbors, + show_ip_eigrp_neighbors_cmd, + "show ip eigrp [vrf NAME] neighbors [IFNAME] [detail]$detail", + SHOW_STR + IP_STR + "IP-EIGRP show commands\n" + VRF_CMD_HELP_STR + "IP-EIGRP neighbors\n" + "Interface to show on\n" + "Detailed Information\n") +{ + struct eigrp *eigrp; + + if (vrf && strncmp(vrf, "all", sizeof("all")) == 0) { + struct vrf *vrf; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + eigrp = eigrp_lookup(vrf->vrf_id); + if (!eigrp) + continue; + + vty_out(vty, "VRF %s:\n", vrf->name); + + eigrp_neighbors_helper(vty, eigrp, ifname, detail); + } + } else { + eigrp = eigrp_vty_get_eigrp(vty, vrf); + if (eigrp == NULL) { + vty_out(vty, " EIGRP Routing Process not enabled\n"); + return CMD_SUCCESS; + } + + eigrp_neighbors_helper(vty, eigrp, ifname, detail); + } + + return CMD_SUCCESS; +} + +/* + * Execute hard restart for all neighbors + */ +DEFPY (clear_ip_eigrp_neighbors, + clear_ip_eigrp_neighbors_cmd, + "clear ip eigrp [vrf NAME] neighbors", + CLEAR_STR + IP_STR + "Clear IP-EIGRP\n" + VRF_CMD_HELP_STR + "Clear IP-EIGRP neighbors\n") +{ + struct eigrp *eigrp; + struct eigrp_interface *ei; + struct listnode *node, *node2, *nnode2; + struct eigrp_neighbor *nbr; + + /* Check if eigrp process is enabled */ + eigrp = eigrp_vty_get_eigrp(vty, vrf); + if (eigrp == NULL) { + vty_out(vty, " EIGRP Routing Process not enabled\n"); + return CMD_SUCCESS; + } + + /* iterate over all eigrp interfaces */ + for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { + /* send Goodbye Hello */ + eigrp_hello_send(ei, EIGRP_HELLO_GRACEFUL_SHUTDOWN, NULL); + + /* iterate over all neighbors on eigrp interface */ + for (ALL_LIST_ELEMENTS(ei->nbrs, node2, nnode2, nbr)) { + if (nbr->state != EIGRP_NEIGHBOR_DOWN) { + zlog_debug( + "Neighbor %pI4 (%s) is down: manually cleared", + &nbr->src, + ifindex2ifname(nbr->ei->ifp->ifindex, + eigrp->vrf_id)); + vty_time_print(vty, 0); + vty_out(vty, + "Neighbor %pI4 (%s) is down: manually cleared\n", + &nbr->src, + ifindex2ifname(nbr->ei->ifp->ifindex, + eigrp->vrf_id)); + + /* set neighbor to DOWN */ + nbr->state = EIGRP_NEIGHBOR_DOWN; + /* delete neighbor */ + eigrp_nbr_delete(nbr); + } + } + } + + return CMD_SUCCESS; +} + +/* + * Execute hard restart for all neighbors on interface + */ +DEFPY (clear_ip_eigrp_neighbors_int, + clear_ip_eigrp_neighbors_int_cmd, + "clear ip eigrp [vrf NAME] neighbors IFNAME", + CLEAR_STR + IP_STR + "Clear IP-EIGRP\n" + VRF_CMD_HELP_STR + "Clear IP-EIGRP neighbors\n" + "Interface's name\n") +{ + struct eigrp *eigrp; + struct eigrp_interface *ei; + struct listnode *node2, *nnode2; + struct eigrp_neighbor *nbr; + + /* Check if eigrp process is enabled */ + eigrp = eigrp_vty_get_eigrp(vty, vrf); + if (eigrp == NULL) { + vty_out(vty, " EIGRP Routing Process not enabled\n"); + return CMD_SUCCESS; + } + + /* lookup interface by specified name */ + ei = eigrp_if_lookup_by_name(eigrp, ifname); + if (ei == NULL) { + vty_out(vty, " Interface (%s) doesn't exist\n", ifname); + return CMD_WARNING; + } + + /* send Goodbye Hello */ + eigrp_hello_send(ei, EIGRP_HELLO_GRACEFUL_SHUTDOWN, NULL); + + /* iterate over all neighbors on eigrp interface */ + for (ALL_LIST_ELEMENTS(ei->nbrs, node2, nnode2, nbr)) { + if (nbr->state != EIGRP_NEIGHBOR_DOWN) { + zlog_debug( + "Neighbor %pI4 (%s) is down: manually cleared", + &nbr->src, + ifindex2ifname(nbr->ei->ifp->ifindex, + eigrp->vrf_id)); + vty_time_print(vty, 0); + vty_out(vty, + "Neighbor %pI4 (%s) is down: manually cleared\n", + &nbr->src, + ifindex2ifname(nbr->ei->ifp->ifindex, + eigrp->vrf_id)); + + /* set neighbor to DOWN */ + nbr->state = EIGRP_NEIGHBOR_DOWN; + /* delete neighbor */ + eigrp_nbr_delete(nbr); + } + } + + return CMD_SUCCESS; +} + +/* + * Execute hard restart for neighbor specified by IP + */ +DEFPY (clear_ip_eigrp_neighbors_IP, + clear_ip_eigrp_neighbors_IP_cmd, + "clear ip eigrp [vrf NAME] neighbors A.B.C.D$nbr_addr", + CLEAR_STR + IP_STR + "Clear IP-EIGRP\n" + VRF_CMD_HELP_STR + "Clear IP-EIGRP neighbors\n" + "IP-EIGRP neighbor address\n") +{ + struct eigrp *eigrp; + struct eigrp_neighbor *nbr; + + /* Check if eigrp process is enabled */ + eigrp = eigrp_vty_get_eigrp(vty, vrf); + if (eigrp == NULL) { + vty_out(vty, " EIGRP Routing Process not enabled\n"); + return CMD_SUCCESS; + } + + /* lookup neighbor in whole process */ + nbr = eigrp_nbr_lookup_by_addr_process(eigrp, nbr_addr); + + /* if neighbor doesn't exists, notify user and exit */ + if (nbr == NULL) { + vty_out(vty, "Neighbor with entered address doesn't exists.\n"); + return CMD_WARNING; + } + + /* execute hard reset on neighbor */ + eigrp_nbr_hard_restart(nbr, vty); + + return CMD_SUCCESS; +} + +/* + * Execute graceful restart for all neighbors + */ +DEFPY (clear_ip_eigrp_neighbors_soft, + clear_ip_eigrp_neighbors_soft_cmd, + "clear ip eigrp [vrf NAME] neighbors soft", + CLEAR_STR + IP_STR + "Clear IP-EIGRP\n" + VRF_CMD_HELP_STR + "Clear IP-EIGRP neighbors\n" + "Resync with peers without adjacency reset\n") +{ + struct eigrp *eigrp; + + /* Check if eigrp process is enabled */ + eigrp = eigrp_vty_get_eigrp(vty, vrf); + if (eigrp == NULL) { + vty_out(vty, " EIGRP Routing Process not enabled\n"); + return CMD_SUCCESS; + } + + /* execute graceful restart on all neighbors */ + eigrp_update_send_process_GR(eigrp, EIGRP_GR_MANUAL, vty); + + return CMD_SUCCESS; +} + +/* + * Execute graceful restart for all neighbors on interface + */ +DEFPY (clear_ip_eigrp_neighbors_int_soft, + clear_ip_eigrp_neighbors_int_soft_cmd, + "clear ip eigrp [vrf NAME] neighbors IFNAME soft", + CLEAR_STR + IP_STR + "Clear IP-EIGRP\n" + VRF_CMD_HELP_STR + "Clear IP-EIGRP neighbors\n" + "Interface's name\n" + "Resync with peer without adjacency reset\n") +{ + struct eigrp *eigrp; + struct eigrp_interface *ei; + + /* Check if eigrp process is enabled */ + eigrp = eigrp_vty_get_eigrp(vty, vrf); + if (eigrp == NULL) { + vty_out(vty, " EIGRP Routing Process not enabled\n"); + return CMD_SUCCESS; + } + + /* lookup interface by specified name */ + ei = eigrp_if_lookup_by_name(eigrp, ifname); + if (ei == NULL) { + vty_out(vty, " Interface (%s) doesn't exist\n", argv[4]->arg); + return CMD_WARNING; + } + + /* execute graceful restart for all neighbors on interface */ + eigrp_update_send_interface_GR(ei, EIGRP_GR_MANUAL, vty); + return CMD_SUCCESS; +} + +/* + * Execute graceful restart for neighbor specified by IP + */ +DEFPY (clear_ip_eigrp_neighbors_IP_soft, + clear_ip_eigrp_neighbors_IP_soft_cmd, + "clear ip eigrp [vrf NAME] neighbors A.B.C.D$nbr_addr soft", + CLEAR_STR + IP_STR + "Clear IP-EIGRP\n" + VRF_CMD_HELP_STR + "Clear IP-EIGRP neighbors\n" + "IP-EIGRP neighbor address\n" + "Resync with peer without adjacency reset\n") +{ + struct eigrp *eigrp; + struct eigrp_neighbor *nbr; + + + /* Check if eigrp process is enabled */ + eigrp = eigrp_vty_get_eigrp(vty, vrf); + if (eigrp == NULL) { + vty_out(vty, " EIGRP Routing Process not enabled\n"); + return CMD_SUCCESS; + } + + /* lookup neighbor in whole process */ + nbr = eigrp_nbr_lookup_by_addr_process(eigrp, nbr_addr); + + /* if neighbor doesn't exists, notify user and exit */ + if (nbr == NULL) { + vty_out(vty, "Neighbor with entered address doesn't exists.\n"); + return CMD_WARNING; + } + + /* execute graceful restart on neighbor */ + eigrp_update_send_GR(nbr, EIGRP_GR_MANUAL, vty); + + return CMD_SUCCESS; +} + +void eigrp_vty_show_init(void) +{ + install_element(VIEW_NODE, &show_ip_eigrp_interfaces_cmd); + + install_element(VIEW_NODE, &show_ip_eigrp_neighbors_cmd); + + install_element(VIEW_NODE, &show_ip_eigrp_topology_cmd); + install_element(VIEW_NODE, &show_ip_eigrp_topology_all_cmd); +} + +/* Install EIGRP related vty commands. */ +void eigrp_vty_init(void) +{ + /* commands for manual hard restart */ + install_element(ENABLE_NODE, &clear_ip_eigrp_neighbors_cmd); + install_element(ENABLE_NODE, &clear_ip_eigrp_neighbors_int_cmd); + install_element(ENABLE_NODE, &clear_ip_eigrp_neighbors_IP_cmd); + /* commands for manual graceful restart */ + install_element(ENABLE_NODE, &clear_ip_eigrp_neighbors_soft_cmd); + install_element(ENABLE_NODE, &clear_ip_eigrp_neighbors_int_soft_cmd); + install_element(ENABLE_NODE, &clear_ip_eigrp_neighbors_IP_soft_cmd); +} diff --git a/eigrpd/eigrp_vty.h b/eigrpd/eigrp_vty.h new file mode 100644 index 0000000..fa07a78 --- /dev/null +++ b/eigrpd/eigrp_vty.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP VTY Interface. + * Copyright (C) 2013-2016 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * Frantisek Gazo + * Tomas Hvorkovy + * Martin Kontsek + * Lukas Koribsky + */ + +#ifndef _QUAGGA_EIGRP_VTY_H +#define _QUAGGA_EIGRP_VTY_H + +/* Prototypes. */ +extern void eigrp_vty_init(void); +extern void eigrp_vty_show_init(void); + +#endif /* _Quagga_EIGRP_VTY_H_ */ diff --git a/eigrpd/eigrp_yang.h b/eigrpd/eigrp_yang.h new file mode 100644 index 0000000..b59f5f9 --- /dev/null +++ b/eigrpd/eigrp_yang.h @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP YANG Functions. + * Copyright (C) 2019 + * Authors: + * Donnie Savage + */ + +#ifndef _EIGRP_YANG_H_ +#define _EIGRP_YANG_H_ + +/*Prototypes*/ + +/* eigrp_northbound.c */ +extern const struct frr_yang_module_info frr_eigrpd_info; + +#endif /*EIGRP_YANG_H_ */ diff --git a/eigrpd/eigrp_zebra.c b/eigrpd/eigrp_zebra.c new file mode 100644 index 0000000..a5cecb9 --- /dev/null +++ b/eigrpd/eigrp_zebra.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Zebra connect library for EIGRP. + * Copyright (C) 2013-2014 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + */ + +#include <zebra.h> + +#include "frrevent.h" +#include "command.h" +#include "network.h" +#include "prefix.h" +#include "routemap.h" +#include "table.h" +#include "stream.h" +#include "memory.h" +#include "zclient.h" +#include "filter.h" +#include "plist.h" +#include "log.h" +#include "nexthop.h" + +#include "eigrpd/eigrp_types.h" +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_interface.h" +#include "eigrpd/eigrp_neighbor.h" +#include "eigrpd/eigrp_packet.h" +#include "eigrpd/eigrp_zebra.h" +#include "eigrpd/eigrp_vty.h" +#include "eigrpd/eigrp_dump.h" +#include "eigrpd/eigrp_network.h" +#include "eigrpd/eigrp_topology.h" +#include "eigrpd/eigrp_fsm.h" +#include "eigrpd/eigrp_metric.h" + +static int eigrp_interface_address_add(ZAPI_CALLBACK_ARGS); +static int eigrp_interface_address_delete(ZAPI_CALLBACK_ARGS); + +static int eigrp_zebra_read_route(ZAPI_CALLBACK_ARGS); + +/* Zebra structure to hold current status. */ +struct zclient *zclient = NULL; + +/* For registering threads. */ +extern struct event_loop *master; +struct in_addr router_id_zebra; + +/* Router-id update message from zebra. */ +static int eigrp_router_id_update_zebra(ZAPI_CALLBACK_ARGS) +{ + struct eigrp *eigrp; + struct prefix router_id; + zebra_router_id_update_read(zclient->ibuf, &router_id); + + router_id_zebra = router_id.u.prefix4; + + eigrp = eigrp_lookup(vrf_id); + + if (eigrp != NULL) + eigrp_router_id_update(eigrp); + + return 0; +} + +static int eigrp_zebra_route_notify_owner(ZAPI_CALLBACK_ARGS) +{ + struct prefix p; + enum zapi_route_notify_owner note; + uint32_t table; + + if (!zapi_route_notify_decode(zclient->ibuf, &p, &table, ¬e, NULL, + NULL)) + return -1; + + return 0; +} + +static void eigrp_zebra_connected(struct zclient *zclient) +{ + zclient_send_reg_requests(zclient, VRF_DEFAULT); +} + +static zclient_handler *const eigrp_handlers[] = { + [ZEBRA_ROUTER_ID_UPDATE] = eigrp_router_id_update_zebra, + [ZEBRA_INTERFACE_ADDRESS_ADD] = eigrp_interface_address_add, + [ZEBRA_INTERFACE_ADDRESS_DELETE] = eigrp_interface_address_delete, + [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = eigrp_zebra_read_route, + [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = eigrp_zebra_read_route, + [ZEBRA_ROUTE_NOTIFY_OWNER] = eigrp_zebra_route_notify_owner, +}; + +void eigrp_zebra_init(void) +{ + struct zclient_options opt = {.receive_notify = false}; + + zclient = zclient_new(master, &opt, eigrp_handlers, + array_size(eigrp_handlers)); + + zclient_init(zclient, ZEBRA_ROUTE_EIGRP, 0, &eigrpd_privs); + zclient->zebra_connected = eigrp_zebra_connected; +} + + +/* Zebra route add and delete treatment. */ +static int eigrp_zebra_read_route(ZAPI_CALLBACK_ARGS) +{ + struct zapi_route api; + struct eigrp *eigrp; + + if (zapi_route_decode(zclient->ibuf, &api) < 0) + return -1; + + if (IPV4_NET127(ntohl(api.prefix.u.prefix4.s_addr))) + return 0; + + eigrp = eigrp_lookup(vrf_id); + if (eigrp == NULL) + return 0; + + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { + + } else /* if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_DEL) */ + { + } + + return 0; +} + +static int eigrp_interface_address_add(ZAPI_CALLBACK_ARGS) +{ + struct connected *c; + + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); + + if (c == NULL) + return 0; + + if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) + zlog_debug("Zebra: interface %s address add %pFX", c->ifp->name, + c->address); + + eigrp_if_update(c->ifp); + + return 0; +} + +static int eigrp_interface_address_delete(ZAPI_CALLBACK_ARGS) +{ + struct connected *c; + struct interface *ifp; + struct eigrp_interface *ei; + + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); + + if (c == NULL) + return 0; + + if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) + zlog_debug("Zebra: interface %s address delete %pFX", + c->ifp->name, c->address); + + ifp = c->ifp; + ei = ifp->info; + if (!ei) + return 0; + + /* Call interface hook functions to clean up */ + if (prefix_cmp(&ei->address, c->address) == 0) + eigrp_if_free(ei, INTERFACE_DOWN_BY_ZEBRA); + + connected_free(&c); + + return 0; +} + +void eigrp_zebra_route_add(struct eigrp *eigrp, struct prefix *p, + struct list *successors, uint32_t distance) +{ + struct zapi_route api; + struct zapi_nexthop *api_nh; + struct eigrp_route_descriptor *te; + struct listnode *node; + int count = 0; + + if (!zclient->redist[AFI_IP][ZEBRA_ROUTE_EIGRP]) + return; + + memset(&api, 0, sizeof(api)); + api.vrf_id = eigrp->vrf_id; + api.type = ZEBRA_ROUTE_EIGRP; + api.safi = SAFI_UNICAST; + api.metric = distance; + memcpy(&api.prefix, p, sizeof(*p)); + + SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); + SET_FLAG(api.message, ZAPI_MESSAGE_METRIC); + + /* Nexthop, ifindex, distance and metric information. */ + for (ALL_LIST_ELEMENTS_RO(successors, node, te)) { + if (count >= MULTIPATH_NUM) + break; + api_nh = &api.nexthops[count]; + api_nh->vrf_id = eigrp->vrf_id; + if (te->adv_router->src.s_addr) { + api_nh->gate.ipv4 = te->adv_router->src; + api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; + } else + api_nh->type = NEXTHOP_TYPE_IFINDEX; + api_nh->ifindex = te->ei->ifp->ifindex; + + count++; + } + api.nexthop_num = count; + + if (IS_DEBUG_EIGRP(zebra, ZEBRA_REDISTRIBUTE)) { + zlog_debug("Zebra: Route add %pFX", p); + } + + zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); +} + +void eigrp_zebra_route_delete(struct eigrp *eigrp, struct prefix *p) +{ + struct zapi_route api; + + if (!zclient->redist[AFI_IP][ZEBRA_ROUTE_EIGRP]) + return; + + memset(&api, 0, sizeof(api)); + api.vrf_id = eigrp->vrf_id; + api.type = ZEBRA_ROUTE_EIGRP; + api.safi = SAFI_UNICAST; + memcpy(&api.prefix, p, sizeof(*p)); + zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); + + if (IS_DEBUG_EIGRP(zebra, ZEBRA_REDISTRIBUTE)) + zlog_debug("Zebra: Route del %pFX", p); + + return; +} + +static int eigrp_is_type_redistributed(int type, vrf_id_t vrf_id) +{ + return ((DEFAULT_ROUTE_TYPE(type)) + ? vrf_bitmap_check( + &zclient->default_information[AFI_IP], vrf_id) + : vrf_bitmap_check(&zclient->redist[AFI_IP][type], + vrf_id)); +} + +int eigrp_redistribute_set(struct eigrp *eigrp, int type, + struct eigrp_metrics metric) +{ + + if (eigrp_is_type_redistributed(type, eigrp->vrf_id)) { + if (eigrp_metrics_is_same(metric, eigrp->dmetric[type])) { + eigrp->dmetric[type] = metric; + } + + eigrp_external_routes_refresh(eigrp, type); + + // if (IS_DEBUG_EIGRP(zebra, ZEBRA_REDISTRIBUTE)) + // zlog_debug ("Redistribute[%s]: Refresh Type[%d], + // Metric[%d]", + // eigrp_redist_string(type), + // metric_type (eigrp, type), metric_value + // (eigrp, type)); + return CMD_SUCCESS; + } + + eigrp->dmetric[type] = metric; + + zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, type, 0, + eigrp->vrf_id); + + ++eigrp->redistribute; + + return CMD_SUCCESS; +} + +int eigrp_redistribute_unset(struct eigrp *eigrp, int type) +{ + + if (eigrp_is_type_redistributed(type, eigrp->vrf_id)) { + memset(&eigrp->dmetric[type], 0, sizeof(struct eigrp_metrics)); + zclient_redistribute(ZEBRA_REDISTRIBUTE_DELETE, zclient, AFI_IP, + type, 0, eigrp->vrf_id); + --eigrp->redistribute; + } + + return CMD_SUCCESS; +} diff --git a/eigrpd/eigrp_zebra.h b/eigrpd/eigrp_zebra.h new file mode 100644 index 0000000..927d562 --- /dev/null +++ b/eigrpd/eigrp_zebra.h @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Zebra connect library for EIGRP. + * Copyright (C) 2013-2014 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + */ + +#ifndef _ZEBRA_EIGRP_ZEBRA_H_ +#define _ZEBRA_EIGRP_ZEBRA_H_ + +#include "vty.h" +#include "vrf.h" + +extern void eigrp_zebra_init(void); + +extern void eigrp_zebra_route_add(struct eigrp *eigrp, struct prefix *p, + struct list *successors, uint32_t distance); +extern void eigrp_zebra_route_delete(struct eigrp *eigrp, struct prefix *); +extern int eigrp_redistribute_set(struct eigrp *, int, struct eigrp_metrics); +extern int eigrp_redistribute_unset(struct eigrp *, int); + +#endif /* _ZEBRA_EIGRP_ZEBRA_H_ */ diff --git a/eigrpd/eigrpd.c b/eigrpd/eigrpd.c new file mode 100644 index 0000000..c7dd96b --- /dev/null +++ b/eigrpd/eigrpd.c @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Daemon Program. + * Copyright (C) 2013-2014 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + */ + +#include <zebra.h> + +#include "frrevent.h" +#include "vty.h" +#include "command.h" +#include "linklist.h" +#include "prefix.h" +#include "table.h" +#include "if.h" +#include "memory.h" +#include "stream.h" +#include "log.h" +#include "sockunion.h" /* for inet_aton () */ +#include "zclient.h" +#include "plist.h" +#include "sockopt.h" +#include "keychain.h" +#include "libfrr.h" +#include "lib_errors.h" +#include "distribute.h" + +#include "eigrpd/eigrp_structs.h" +#include "eigrpd/eigrpd.h" +#include "eigrpd/eigrp_interface.h" +#include "eigrpd/eigrp_zebra.h" +#include "eigrpd/eigrp_vty.h" +#include "eigrpd/eigrp_neighbor.h" +#include "eigrpd/eigrp_packet.h" +#include "eigrpd/eigrp_network.h" +#include "eigrpd/eigrp_topology.h" +#include "eigrpd/eigrp_filter.h" + +DEFINE_MGROUP(EIGRPD, "eigrpd"); + +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_TOP, "EIGRP structure"); + +DEFINE_QOBJ_TYPE(eigrp); + +static struct eigrp_master eigrp_master; + +struct eigrp_master *eigrp_om; + +extern struct zclient *zclient; +extern struct in_addr router_id_zebra; + + +/* + * void eigrp_router_id_update(struct eigrp *eigrp) + * + * Description: + * update routerid associated with this instance of EIGRP. + * If the id changes, then call if_update for each interface + * to resync the topology database with all neighbors + * + * Select the router ID based on these priorities: + * 1. Statically assigned router ID is always the first choice. + * 2. If there is no statically assigned router ID, then try to stick + * with the most recent value, since changing router ID's is very + * disruptive. + * 3. Last choice: just go with whatever the zebra daemon recommends. + * + * Note: + * router id for EIGRP is really just a 32 bit number. Cisco historically + * displays it in dotted decimal notation, and will pickup an IP address + * from an interface so it can be 'auto-configed" to a uniqe value + * + * This does not work for IPv6, and to make the code simpler, its + * stored and processed internerall as a 32bit number + */ +void eigrp_router_id_update(struct eigrp *eigrp) +{ + struct vrf *vrf = vrf_lookup_by_id(eigrp->vrf_id); + struct interface *ifp; + struct in_addr router_id, router_id_old; + + router_id_old = eigrp->router_id; + + if (eigrp->router_id_static.s_addr != INADDR_ANY) + router_id = eigrp->router_id_static; + + else if (eigrp->router_id.s_addr != INADDR_ANY) + router_id = eigrp->router_id; + + else + router_id = router_id_zebra; + + eigrp->router_id = router_id; + if (router_id_old.s_addr != router_id.s_addr) { + /* update eigrp_interface's */ + FOR_ALL_INTERFACES (vrf, ifp) + eigrp_if_update(ifp); + } +} + +void eigrp_master_init(void) +{ + struct timeval tv; + + memset(&eigrp_master, 0, sizeof(eigrp_master)); + + eigrp_om = &eigrp_master; + eigrp_om->eigrp = list_new(); + + monotime(&tv); + eigrp_om->start_time = tv.tv_sec; +} + +/* Allocate new eigrp structure. */ +static struct eigrp *eigrp_new(uint16_t as, vrf_id_t vrf_id) +{ + struct eigrp *eigrp = XCALLOC(MTYPE_EIGRP_TOP, sizeof(struct eigrp)); + + /* init information relevant to peers */ + eigrp->vrf_id = vrf_id; + eigrp->vrid = 0; + eigrp->AS = as; + eigrp->router_id.s_addr = INADDR_ANY; + eigrp->router_id_static.s_addr = INADDR_ANY; + eigrp->sequence_number = 1; + + /*Configure default K Values for EIGRP Process*/ + eigrp->k_values[0] = EIGRP_K1_DEFAULT; + eigrp->k_values[1] = EIGRP_K2_DEFAULT; + eigrp->k_values[2] = EIGRP_K3_DEFAULT; + eigrp->k_values[3] = EIGRP_K4_DEFAULT; + eigrp->k_values[4] = EIGRP_K5_DEFAULT; + eigrp->k_values[5] = EIGRP_K6_DEFAULT; + + /* init internal data structures */ + eigrp->eiflist = list_new(); + eigrp->passive_interface_default = EIGRP_IF_ACTIVE; + eigrp->networks = eigrp_topology_new(); + + eigrp->fd = eigrp_sock_init(vrf_lookup_by_id(vrf_id)); + + if (eigrp->fd < 0) { + flog_err_sys( + EC_LIB_SOCKET, + "%s: fatal error: eigrp_sock_init was unable to open a socket", + __func__); + exit(1); + } + + eigrp->maxsndbuflen = getsockopt_so_sendbuf(eigrp->fd); + + eigrp->ibuf = stream_new(EIGRP_PACKET_MAX_LEN + 1); + + event_add_read(master, eigrp_read, eigrp, eigrp->fd, &eigrp->t_read); + eigrp->oi_write_q = list_new(); + + eigrp->topology_table = route_table_init(); + + eigrp->neighbor_self = eigrp_nbr_new(NULL); + eigrp->neighbor_self->src.s_addr = INADDR_ANY; + + eigrp->variance = EIGRP_VARIANCE_DEFAULT; + eigrp->max_paths = EIGRP_MAX_PATHS_DEFAULT; + + eigrp->serno = 0; + eigrp->serno_last_update = 0; + eigrp->topology_changes_externalIPV4 = list_new(); + eigrp->topology_changes_internalIPV4 = list_new(); + + eigrp->list[EIGRP_FILTER_IN] = NULL; + eigrp->list[EIGRP_FILTER_OUT] = NULL; + + eigrp->prefix[EIGRP_FILTER_IN] = NULL; + eigrp->prefix[EIGRP_FILTER_OUT] = NULL; + + eigrp->routemap[EIGRP_FILTER_IN] = NULL; + eigrp->routemap[EIGRP_FILTER_OUT] = NULL; + + /* Distribute list install. */ + eigrp->distribute_ctx = + distribute_list_ctx_create(vrf_lookup_by_id(eigrp->vrf_id)); + distribute_list_add_hook(eigrp->distribute_ctx, + eigrp_distribute_update); + distribute_list_delete_hook(eigrp->distribute_ctx, + eigrp_distribute_update); + + /* + eigrp->if_rmap_ctx = if_rmap_ctx_create(eigrp->vrf_id); + if_rmap_hook_add (eigrp_if_rmap_update); + if_rmap_hook_delete (eigrp_if_rmap_update); + */ + QOBJ_REG(eigrp, eigrp); + return eigrp; +} + +struct eigrp *eigrp_get(uint16_t as, vrf_id_t vrf_id) +{ + struct eigrp *eigrp; + + eigrp = eigrp_lookup(vrf_id); + if (eigrp == NULL) { + eigrp = eigrp_new(as, vrf_id); + listnode_add(eigrp_om->eigrp, eigrp); + } + + return eigrp; +} + +/* Shut down the entire process */ +void eigrp_terminate(void) +{ + struct eigrp *eigrp; + struct listnode *node, *nnode; + + /* shutdown already in progress */ + if (CHECK_FLAG(eigrp_om->options, EIGRP_MASTER_SHUTDOWN)) + return; + + SET_FLAG(eigrp_om->options, EIGRP_MASTER_SHUTDOWN); + + for (ALL_LIST_ELEMENTS(eigrp_om->eigrp, node, nnode, eigrp)) + eigrp_finish(eigrp); + + frr_fini(); +} + +void eigrp_finish(struct eigrp *eigrp) +{ + eigrp_finish_final(eigrp); + + /* eigrp being shut-down? If so, was this the last eigrp instance? */ + if (CHECK_FLAG(eigrp_om->options, EIGRP_MASTER_SHUTDOWN) + && (listcount(eigrp_om->eigrp) == 0)) { + if (zclient) { + zclient_stop(zclient); + zclient_free(zclient); + } + exit(0); + } + + return; +} + +/* Final cleanup of eigrp instance */ +void eigrp_finish_final(struct eigrp *eigrp) +{ + struct eigrp_interface *ei; + struct eigrp_neighbor *nbr; + struct listnode *node, *nnode, *node2, *nnode2; + + for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, ei)) { + for (ALL_LIST_ELEMENTS(ei->nbrs, node2, nnode2, nbr)) + eigrp_nbr_delete(nbr); + eigrp_if_free(ei, INTERFACE_DOWN_BY_FINAL); + } + + EVENT_OFF(eigrp->t_write); + EVENT_OFF(eigrp->t_read); + close(eigrp->fd); + + list_delete(&eigrp->eiflist); + list_delete(&eigrp->oi_write_q); + + eigrp_topology_free(eigrp, eigrp->topology_table); + + eigrp_nbr_delete(eigrp->neighbor_self); + + list_delete(&eigrp->topology_changes_externalIPV4); + list_delete(&eigrp->topology_changes_internalIPV4); + + listnode_delete(eigrp_om->eigrp, eigrp); + + stream_free(eigrp->ibuf); + distribute_list_delete(&eigrp->distribute_ctx); + XFREE(MTYPE_EIGRP_TOP, eigrp); +} + +/*Look for existing eigrp process*/ +struct eigrp *eigrp_lookup(vrf_id_t vrf_id) +{ + struct eigrp *eigrp; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(eigrp_om->eigrp, node, nnode, eigrp)) + if (eigrp->vrf_id == vrf_id) + return eigrp; + + return NULL; +} diff --git a/eigrpd/eigrpd.h b/eigrpd/eigrpd.h new file mode 100644 index 0000000..a6a4e39 --- /dev/null +++ b/eigrpd/eigrpd.h @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP main header. + * Copyright (C) 2013-2014 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + */ + +#ifndef _ZEBRA_EIGRPD_H +#define _ZEBRA_EIGRPD_H + +#include <zebra.h> + +#include "filter.h" +#include "log.h" +#include "memory.h" + +DECLARE_MGROUP(EIGRPD); + +/* Set EIGRP version is "classic" - wide metrics comes next */ +#define EIGRP_MAJOR_VERSION 1 +#define EIGRP_MINOR_VERSION 2 + +#define EIGRP_TLV_32B_VERSION 1 /* Original 32bit scaled metrics */ +#define EIGRP_TLV_64B_VERSION 2 /* Current 64bit 'wide' metrics */ +#define EIGRP_TLV_MTR_VERSION 3 /* MTR TLVs with 32bit metric *Not Supported */ +#define EIGRP_TLV_SAF_VERSION 4 /* SAF TLVs with 64bit metric *Not Supported */ + +struct eigrp_master { + /* EIGRP instance. */ + struct list *eigrp; + + /* EIGRP thread master. */ + struct event_loop *master; + + /* Zebra interface list. */ + struct list *iflist; + + /* EIGRP start time. */ + time_t start_time; + + /* Various EIGRP global configuration. */ + uint8_t options; + +#define EIGRP_MASTER_SHUTDOWN (1 << 0) /* deferred-shutdown */ +}; + +/* Extern variables. */ +extern struct zclient *zclient; +extern struct event_loop *master; +extern struct eigrp_master *eigrp_om; +extern struct zebra_privs_t eigrpd_privs; + +/* Prototypes */ +extern void eigrp_master_init(void); +extern void eigrp_terminate(void); +extern void eigrp_finish_final(struct eigrp *eigrp); +extern void eigrp_finish(struct eigrp *eigrp); +extern struct eigrp *eigrp_get(uint16_t as, vrf_id_t vrf_id); +extern struct eigrp *eigrp_lookup(vrf_id_t vrf_id); +extern void eigrp_router_id_update(struct eigrp *eigrp); + +#endif /* _ZEBRA_EIGRPD_H */ diff --git a/eigrpd/subdir.am b/eigrpd/subdir.am new file mode 100644 index 0000000..e417132 --- /dev/null +++ b/eigrpd/subdir.am @@ -0,0 +1,75 @@ +# +# eigrpd +# + +if EIGRPD +sbin_PROGRAMS += eigrpd/eigrpd +vtysh_daemons += eigrpd +man8 += $(MANBUILD)/frr-eigrpd.8 +endif + +eigrpd_eigrpd_SOURCES = \ + eigrpd/eigrp_cli.c \ + eigrpd/eigrp_dump.c \ + eigrpd/eigrp_errors.c \ + eigrpd/eigrp_filter.c \ + eigrpd/eigrp_fsm.c \ + eigrpd/eigrp_hello.c \ + eigrpd/eigrp_interface.c \ + eigrpd/eigrp_main.c \ + eigrpd/eigrp_metric.c \ + eigrpd/eigrp_neighbor.c \ + eigrpd/eigrp_network.c \ + eigrpd/eigrp_northbound.c \ + eigrpd/eigrp_packet.c \ + eigrpd/eigrp_query.c \ + eigrpd/eigrp_reply.c \ + eigrpd/eigrp_siaquery.c \ + eigrpd/eigrp_siareply.c \ + eigrpd/eigrp_snmp.c \ + eigrpd/eigrp_topology.c \ + eigrpd/eigrp_update.c \ + eigrpd/eigrp_vrf.c \ + eigrpd/eigrp_vty.c \ + eigrpd/eigrp_zebra.c \ + eigrpd/eigrpd.c \ + # end + +eigrpdheaderdir = $(pkgincludedir)/eigrpd +eigrpdheader_HEADERS = \ + eigrpd/eigrp_dump.h \ + eigrpd/eigrp_topology.h \ + eigrpd/eigrpd.h \ + # end + +clippy_scan += \ + eigrpd/eigrp_cli.c \ + eigrpd/eigrp_vty.c \ + # end + +noinst_HEADERS += \ + eigrpd/eigrp_cli.h \ + eigrpd/eigrp_const.h \ + eigrpd/eigrp_errors.h \ + eigrpd/eigrp_filter.h \ + eigrpd/eigrp_fsm.h \ + eigrpd/eigrp_interface.h \ + eigrpd/eigrp_macros.h \ + eigrpd/eigrp_metric.h \ + eigrpd/eigrp_neighbor.h \ + eigrpd/eigrp_network.h \ + eigrpd/eigrp_packet.h \ + eigrpd/eigrp_snmp.h \ + eigrpd/eigrp_structs.h \ + eigrpd/eigrp_types.h \ + eigrpd/eigrp_vrf.h \ + eigrpd/eigrp_vty.h \ + eigrpd/eigrp_yang.h \ + eigrpd/eigrp_zebra.h \ + # end + +nodist_eigrpd_eigrpd_SOURCES = \ + yang/frr-eigrpd.yang.c \ + # end + +eigrpd_eigrpd_LDADD = lib/libfrr.la $(LIBCAP) |