summaryrefslogtreecommitdiffstats
path: root/eigrpd/eigrp_fsm.c
diff options
context:
space:
mode:
Diffstat (limited to 'eigrpd/eigrp_fsm.c')
-rw-r--r--eigrpd/eigrp_fsm.c628
1 files changed, 628 insertions, 0 deletions
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;
+}