summaryrefslogtreecommitdiffstats
path: root/eigrpd
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
commite2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch)
treef0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /eigrpd
parentInitial commit. (diff)
downloadfrr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz
frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'eigrpd')
-rw-r--r--eigrpd/.gitignore2
-rw-r--r--eigrpd/Makefile10
-rw-r--r--eigrpd/eigrp_cli.c898
-rw-r--r--eigrpd/eigrp_cli.h66
-rw-r--r--eigrpd/eigrp_const.h439
-rw-r--r--eigrpd/eigrp_dump.c566
-rw-r--r--eigrpd/eigrp_dump.h147
-rw-r--r--eigrpd/eigrp_errors.c36
-rw-r--r--eigrpd/eigrp_errors.h20
-rw-r--r--eigrpd/eigrp_filter.c275
-rw-r--r--eigrpd/eigrp_filter.h29
-rw-r--r--eigrpd/eigrp_fsm.c628
-rw-r--r--eigrpd/eigrp_fsm.h19
-rw-r--r--eigrpd/eigrp_hello.c783
-rw-r--r--eigrpd/eigrp_interface.c498
-rw-r--r--eigrpd/eigrp_interface.h46
-rw-r--r--eigrpd/eigrp_macros.h32
-rw-r--r--eigrpd/eigrp_main.c220
-rw-r--r--eigrpd/eigrp_metric.c138
-rw-r--r--eigrpd/eigrp_metric.h48
-rw-r--r--eigrpd/eigrp_neighbor.c337
-rw-r--r--eigrpd/eigrp_neighbor.h44
-rw-r--r--eigrpd/eigrp_network.c336
-rw-r--r--eigrpd/eigrp_network.h33
-rw-r--r--eigrpd/eigrp_northbound.c1523
-rw-r--r--eigrpd/eigrp_packet.c1333
-rw-r--r--eigrpd/eigrp_packet.h159
-rw-r--r--eigrpd/eigrp_pkt_tlv1.c0
-rw-r--r--eigrpd/eigrp_pkt_tlv2.c0
-rw-r--r--eigrpd/eigrp_query.c255
-rw-r--r--eigrpd/eigrp_reply.c188
-rw-r--r--eigrpd/eigrp_routemap.c1210
-rw-r--r--eigrpd/eigrp_routemap.h23
-rw-r--r--eigrpd/eigrp_siaquery.c143
-rw-r--r--eigrpd/eigrp_siareply.c142
-rw-r--r--eigrpd/eigrp_snmp.c1312
-rw-r--r--eigrpd/eigrp_snmp.h20
-rw-r--r--eigrpd/eigrp_structs.h494
-rw-r--r--eigrpd/eigrp_topology.c530
-rw-r--r--eigrpd/eigrp_topology.h70
-rw-r--r--eigrpd/eigrp_types.h21
-rw-r--r--eigrpd/eigrp_update.c1038
-rw-r--r--eigrpd/eigrp_vrf.c37
-rw-r--r--eigrpd/eigrp_vrf.h10
-rw-r--r--eigrpd/eigrp_vty.c587
-rw-r--r--eigrpd/eigrp_vty.h24
-rw-r--r--eigrpd/eigrp_yang.h17
-rw-r--r--eigrpd/eigrp_zebra.c299
-rw-r--r--eigrpd/eigrp_zebra.h27
-rw-r--r--eigrpd/eigrpd.c295
-rw-r--r--eigrpd/eigrpd.h67
-rw-r--r--eigrpd/subdir.am75
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, &note, 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)