2 * EIGRPd Finite State Machine (DUAL).
3 * Copyright (C) 2013-2014
11 * This file is part of GNU Zebra.
13 * GNU Zebra is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the
15 * Free Software Foundation; either version 2, or (at your option) any
18 * GNU Zebra is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
23 * You should have received a copy of the GNU General Public License along
24 * with this program; see the file COPYING; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27 * This file contains functions for executing logic of finite state machine
32 * +=====================================+
36 * +=====================================+
38 * (3)| | (1)| | (1)| |
39 * | (0)| | (3)| | (2)|
40 * | | | | | +---------------+
42 * +--------+ | | | +-----------------+ \
47 * +===========+ (6) +===========+ +===========+ (6) +===========+
48 * | |------->| | (5) | |-------->| |
49 * | | (4) | |------>| | (4) | |
50 * | ACTIVE 0 |<-------| ACTIVE 1 | | ACTIVE 2 |<--------| ACTIVE 3
52 * +--| | +--| | +--| | +--| |
53 * | +===========+ | +===========+ | +===========+ |
55 * | ^ |(5) | ^ | ^ ^ | ^
56 * | | +---------|------|------------|----+ | | |
57 * +-------+ +------+ +---------+ +---------+
60 * 0- input event other than query from successor, FC not satisfied
61 * 1- last reply, FD is reset
62 * 2- query from successor, FC not satisfied
63 * 3- last reply, FC satisfied with current value of FDij
64 * 4- distance increase while in active state
65 * 5- query from successor while in active state
66 * 6- last reply, FC not satisfied with current value of FDij
67 * 7- state not changed, usually by receiving not last reply
80 #include "eigrpd/eigrp_types.h"
81 #include "eigrpd/eigrp_structs.h"
82 #include "eigrpd/eigrpd.h"
83 #include "eigrpd/eigrp_interface.h"
84 #include "eigrpd/eigrp_neighbor.h"
85 #include "eigrpd/eigrp_packet.h"
86 #include "eigrpd/eigrp_zebra.h"
87 #include "eigrpd/eigrp_vty.h"
88 #include "eigrpd/eigrp_network.h"
89 #include "eigrpd/eigrp_dump.h"
90 #include "eigrpd/eigrp_topology.h"
91 #include "eigrpd/eigrp_fsm.h"
92 #include "eigrpd/eigrp_metric.h"
97 int eigrp_fsm_event_keep_state(struct eigrp_fsm_action_message
*);
98 int eigrp_fsm_event_nq_fcn(struct eigrp_fsm_action_message
*);
99 int eigrp_fsm_event_q_fcn(struct eigrp_fsm_action_message
*);
100 int eigrp_fsm_event_lr(struct eigrp_fsm_action_message
*);
101 int eigrp_fsm_event_dinc(struct eigrp_fsm_action_message
*);
102 int eigrp_fsm_event_lr_fcs(struct eigrp_fsm_action_message
*);
103 int eigrp_fsm_event_lr_fcn(struct eigrp_fsm_action_message
*);
104 int eigrp_fsm_event_qact(struct eigrp_fsm_action_message
*);
106 //---------------------------------------------------------------------
109 * NSM - field of fields of struct containing one function each.
110 * Which function is used depends on actual state of FSM and occurred
111 * event(arrow in diagram). Usage:
112 * NSM[actual/starting state][occurred event].func
113 * Functions are should be executed within separate thread.
116 int (*func
)(struct eigrp_fsm_action_message
*);
117 } NSM
[EIGRP_FSM_STATE_MAX
][EIGRP_FSM_EVENT_MAX
] = {
120 {eigrp_fsm_event_nq_fcn
}, /* Event 0 */
121 {eigrp_fsm_event_keep_state
}, /* Event 1 */
122 {eigrp_fsm_event_q_fcn
}, /* Event 2 */
123 {eigrp_fsm_event_keep_state
}, /* Event 3 */
124 {eigrp_fsm_event_keep_state
}, /* Event 4 */
125 {eigrp_fsm_event_keep_state
}, /* Event 5 */
126 {eigrp_fsm_event_keep_state
}, /* Event 6 */
127 {eigrp_fsm_event_keep_state
}, /* Event 7 */
131 {eigrp_fsm_event_keep_state
}, /* Event 0 */
132 {eigrp_fsm_event_keep_state
}, /* Event 1 */
133 {eigrp_fsm_event_keep_state
}, /* Event 2 */
134 {eigrp_fsm_event_lr_fcs
}, /* Event 3 */
135 {eigrp_fsm_event_keep_state
}, /* Event 4 */
136 {eigrp_fsm_event_qact
}, /* Event 5 */
137 {eigrp_fsm_event_lr_fcn
}, /* Event 6 */
138 {eigrp_fsm_event_keep_state
}, /* Event 7 */
142 {eigrp_fsm_event_keep_state
}, /* Event 0 */
143 {eigrp_fsm_event_lr
}, /* Event 1 */
144 {eigrp_fsm_event_keep_state
}, /* Event 2 */
145 {eigrp_fsm_event_keep_state
}, /* Event 3 */
146 {eigrp_fsm_event_dinc
}, /* Event 4 */
147 {eigrp_fsm_event_qact
}, /* Event 5 */
148 {eigrp_fsm_event_keep_state
}, /* Event 6 */
149 {eigrp_fsm_event_keep_state
}, /* Event 7 */
153 {eigrp_fsm_event_keep_state
}, /* Event 0 */
154 {eigrp_fsm_event_keep_state
}, /* Event 1 */
155 {eigrp_fsm_event_keep_state
}, /* Event 2 */
156 {eigrp_fsm_event_lr_fcs
}, /* Event 3 */
157 {eigrp_fsm_event_keep_state
}, /* Event 4 */
158 {eigrp_fsm_event_keep_state
}, /* Event 5 */
159 {eigrp_fsm_event_lr_fcn
}, /* Event 6 */
160 {eigrp_fsm_event_keep_state
}, /* Event 7 */
164 {eigrp_fsm_event_keep_state
}, /* Event 0 */
165 {eigrp_fsm_event_lr
}, /* Event 1 */
166 {eigrp_fsm_event_keep_state
}, /* Event 2 */
167 {eigrp_fsm_event_keep_state
}, /* Event 3 */
168 {eigrp_fsm_event_dinc
}, /* Event 4 */
169 {eigrp_fsm_event_keep_state
}, /* Event 5 */
170 {eigrp_fsm_event_keep_state
}, /* Event 6 */
171 {eigrp_fsm_event_keep_state
}, /* Event 7 */
175 static const char *packet_type2str(uint8_t packet_type
)
177 if (packet_type
== EIGRP_OPC_UPDATE
)
179 if (packet_type
== EIGRP_OPC_REQUEST
)
181 if (packet_type
== EIGRP_OPC_QUERY
)
183 if (packet_type
== EIGRP_OPC_REPLY
)
185 if (packet_type
== EIGRP_OPC_HELLO
)
187 if (packet_type
== EIGRP_OPC_IPXSAP
)
189 if (packet_type
== EIGRP_OPC_ACK
)
191 if (packet_type
== EIGRP_OPC_SIAQUERY
)
193 if (packet_type
== EIGRP_OPC_SIAREPLY
)
199 static const char *prefix_state2str(enum eigrp_fsm_states state
)
202 case EIGRP_FSM_STATE_PASSIVE
:
204 case EIGRP_FSM_STATE_ACTIVE_0
:
205 return "Active oij0";
206 case EIGRP_FSM_STATE_ACTIVE_1
:
207 return "Active oij1";
208 case EIGRP_FSM_STATE_ACTIVE_2
:
209 return "Active oij2";
210 case EIGRP_FSM_STATE_ACTIVE_3
:
211 return "Active oij3";
217 static const char *fsm_state2str(enum eigrp_fsm_events event
)
220 case EIGRP_FSM_KEEP_STATE
:
221 return "Keep State Event";
222 case EIGRP_FSM_EVENT_NQ_FCN
:
223 return "Non Query Event Feasability not satisfied";
224 case EIGRP_FSM_EVENT_LR
:
225 return "Last Reply Event";
226 case EIGRP_FSM_EVENT_Q_FCN
:
227 return "Query Event Feasability not satisfied";
228 case EIGRP_FSM_EVENT_LR_FCS
:
229 return "Last Reply Event Feasability satisfied";
230 case EIGRP_FSM_EVENT_DINC
:
231 return "Distance Increase Event";
232 case EIGRP_FSM_EVENT_QACT
:
233 return "Query from Successor while in active state";
234 case EIGRP_FSM_EVENT_LR_FCN
:
235 return "Last Reply Event, Feasibility not satisfied";
241 static const char *change2str(enum metric_change change
)
244 case METRIC_DECREASE
:
248 case METRIC_INCREASE
:
255 * Main function in which are make decisions which event occurred.
256 * msg - argument of type struct eigrp_fsm_action_message contain
257 * details about what happen
259 * Return number of occurred event (arrow in diagram).
262 static enum eigrp_fsm_events
263 eigrp_get_fsm_event(struct eigrp_fsm_action_message
*msg
)
265 // Loading base information from message
266 // struct eigrp *eigrp = msg->eigrp;
267 struct eigrp_prefix_descriptor
*prefix
= msg
->prefix
;
268 struct eigrp_route_descriptor
*entry
= msg
->entry
;
269 uint8_t actual_state
= prefix
->state
;
270 enum metric_change change
;
273 entry
= eigrp_route_descriptor_new();
274 entry
->adv_router
= msg
->adv_router
;
275 entry
->ei
= msg
->adv_router
->ei
;
276 entry
->prefix
= prefix
;
281 * Calculate resultant metrics and insert to correct position
284 change
= eigrp_topology_update_distance(msg
);
286 /* Store for display later */
287 msg
->change
= change
;
289 switch (actual_state
) {
290 case EIGRP_FSM_STATE_PASSIVE
: {
291 struct eigrp_route_descriptor
*head
=
292 listnode_head(prefix
->entries
);
294 if (head
->reported_distance
< prefix
->fdistance
) {
295 return EIGRP_FSM_KEEP_STATE
;
298 * if best entry doesn't satisfy feasibility condition it means
299 * move to active state
300 * dependently if it was query from successor
302 if (msg
->packet_type
== EIGRP_OPC_QUERY
) {
303 return EIGRP_FSM_EVENT_Q_FCN
;
305 return EIGRP_FSM_EVENT_NQ_FCN
;
310 case EIGRP_FSM_STATE_ACTIVE_0
: {
311 if (msg
->packet_type
== EIGRP_OPC_REPLY
) {
312 struct eigrp_route_descriptor
*head
=
313 listnode_head(prefix
->entries
);
315 listnode_delete(prefix
->rij
, entry
->adv_router
);
316 if (prefix
->rij
->count
)
317 return EIGRP_FSM_KEEP_STATE
;
319 zlog_info("All reply received");
320 if (head
->reported_distance
< prefix
->fdistance
) {
321 return EIGRP_FSM_EVENT_LR_FCS
;
324 return EIGRP_FSM_EVENT_LR_FCN
;
325 } else if (msg
->packet_type
== EIGRP_OPC_QUERY
327 & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG
)) {
328 return EIGRP_FSM_EVENT_QACT
;
331 return EIGRP_FSM_KEEP_STATE
;
335 case EIGRP_FSM_STATE_ACTIVE_1
: {
336 if (msg
->packet_type
== EIGRP_OPC_QUERY
337 && (entry
->flags
& EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG
)) {
338 return EIGRP_FSM_EVENT_QACT
;
339 } else if (msg
->packet_type
== EIGRP_OPC_REPLY
) {
340 listnode_delete(prefix
->rij
, entry
->adv_router
);
342 if (change
== METRIC_INCREASE
344 & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG
)) {
345 return EIGRP_FSM_EVENT_DINC
;
346 } else if (prefix
->rij
->count
) {
347 return EIGRP_FSM_KEEP_STATE
;
349 zlog_info("All reply received");
350 return EIGRP_FSM_EVENT_LR
;
352 } else if (msg
->packet_type
== EIGRP_OPC_UPDATE
353 && change
== METRIC_INCREASE
355 & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG
)) {
356 return EIGRP_FSM_EVENT_DINC
;
358 return EIGRP_FSM_KEEP_STATE
;
362 case EIGRP_FSM_STATE_ACTIVE_2
: {
363 if (msg
->packet_type
== EIGRP_OPC_REPLY
) {
364 struct eigrp_route_descriptor
*head
=
365 listnode_head(prefix
->entries
);
367 listnode_delete(prefix
->rij
, entry
->adv_router
);
368 if (prefix
->rij
->count
) {
369 return EIGRP_FSM_KEEP_STATE
;
371 zlog_info("All reply received");
372 if (head
->reported_distance
373 < prefix
->fdistance
) {
374 return EIGRP_FSM_EVENT_LR_FCS
;
377 return EIGRP_FSM_EVENT_LR_FCN
;
380 return EIGRP_FSM_KEEP_STATE
;
384 case EIGRP_FSM_STATE_ACTIVE_3
: {
385 if (msg
->packet_type
== EIGRP_OPC_REPLY
) {
386 listnode_delete(prefix
->rij
, entry
->adv_router
);
388 if (change
== METRIC_INCREASE
390 & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG
)) {
391 return EIGRP_FSM_EVENT_DINC
;
392 } else if (prefix
->rij
->count
) {
393 return EIGRP_FSM_KEEP_STATE
;
395 zlog_info("All reply received");
396 return EIGRP_FSM_EVENT_LR
;
398 } else if (msg
->packet_type
== EIGRP_OPC_UPDATE
399 && change
== METRIC_INCREASE
401 & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG
)) {
402 return EIGRP_FSM_EVENT_DINC
;
404 return EIGRP_FSM_KEEP_STATE
;
410 return EIGRP_FSM_KEEP_STATE
;
414 * Function made to execute in separate thread.
415 * Load argument from thread and execute proper NSM function
417 int eigrp_fsm_event(struct eigrp_fsm_action_message
*msg
)
419 enum eigrp_fsm_events event
= eigrp_get_fsm_event(msg
);
422 "EIGRP AS: %d State: %s Event: %s Network: %pI4 Packet Type: %s Reply RIJ Count: %d change: %s",
423 msg
->eigrp
->AS
, prefix_state2str(msg
->prefix
->state
),
424 fsm_state2str(event
), &msg
->prefix
->destination
->u
.prefix4
,
425 packet_type2str(msg
->packet_type
), msg
->prefix
->rij
->count
,
426 change2str(msg
->change
));
427 (*(NSM
[msg
->prefix
->state
][event
].func
))(msg
);
433 * Function of event 0.
436 int eigrp_fsm_event_nq_fcn(struct eigrp_fsm_action_message
*msg
)
438 struct eigrp
*eigrp
= msg
->eigrp
;
439 struct eigrp_prefix_descriptor
*prefix
= msg
->prefix
;
440 struct list
*successors
= eigrp_topology_get_successor(prefix
);
441 struct eigrp_route_descriptor
*ne
;
443 assert(successors
); // If this is NULL we have shit the bed, fun huh?
445 ne
= listnode_head(successors
);
446 prefix
->state
= EIGRP_FSM_STATE_ACTIVE_1
;
447 prefix
->rdistance
= prefix
->distance
= prefix
->fdistance
= ne
->distance
;
448 prefix
->reported_metric
= ne
->total_metric
;
450 if (eigrp_nbr_count_get(eigrp
)) {
451 prefix
->req_action
|= EIGRP_FSM_NEED_QUERY
;
452 listnode_add(eigrp
->topology_changes_internalIPV4
, prefix
);
454 eigrp_fsm_event_lr(msg
); // in the case that there are no more
458 list_delete(&successors
);
463 int eigrp_fsm_event_q_fcn(struct eigrp_fsm_action_message
*msg
)
465 struct eigrp
*eigrp
= msg
->eigrp
;
466 struct eigrp_prefix_descriptor
*prefix
= msg
->prefix
;
467 struct list
*successors
= eigrp_topology_get_successor(prefix
);
468 struct eigrp_route_descriptor
*ne
;
470 assert(successors
); // If this is NULL somebody poked us in the eye.
472 ne
= listnode_head(successors
);
473 prefix
->state
= EIGRP_FSM_STATE_ACTIVE_3
;
474 prefix
->rdistance
= prefix
->distance
= prefix
->fdistance
= ne
->distance
;
475 prefix
->reported_metric
= ne
->total_metric
;
476 if (eigrp_nbr_count_get(eigrp
)) {
477 prefix
->req_action
|= EIGRP_FSM_NEED_QUERY
;
478 listnode_add(eigrp
->topology_changes_internalIPV4
, prefix
);
480 eigrp_fsm_event_lr(msg
); // in the case that there are no more
484 list_delete(&successors
);
489 int eigrp_fsm_event_keep_state(struct eigrp_fsm_action_message
*msg
)
491 struct eigrp
*eigrp
= msg
->eigrp
;
492 struct eigrp_prefix_descriptor
*prefix
= msg
->prefix
;
493 struct eigrp_route_descriptor
*ne
= listnode_head(prefix
->entries
);
495 if (prefix
->state
== EIGRP_FSM_STATE_PASSIVE
) {
496 if (!eigrp_metrics_is_same(prefix
->reported_metric
,
498 prefix
->rdistance
= prefix
->fdistance
=
499 prefix
->distance
= ne
->distance
;
500 prefix
->reported_metric
= ne
->total_metric
;
501 if (msg
->packet_type
== EIGRP_OPC_QUERY
)
502 eigrp_send_reply(msg
->adv_router
, prefix
);
503 prefix
->req_action
|= EIGRP_FSM_NEED_UPDATE
;
504 listnode_add(eigrp
->topology_changes_internalIPV4
,
507 eigrp_topology_update_node_flags(eigrp
, prefix
);
508 eigrp_update_routing_table(eigrp
, prefix
);
511 if (msg
->packet_type
== EIGRP_OPC_QUERY
)
512 eigrp_send_reply(msg
->adv_router
, prefix
);
517 int eigrp_fsm_event_lr(struct eigrp_fsm_action_message
*msg
)
519 struct eigrp
*eigrp
= msg
->eigrp
;
520 struct eigrp_prefix_descriptor
*prefix
= msg
->prefix
;
521 struct eigrp_route_descriptor
*ne
= listnode_head(prefix
->entries
);
523 prefix
->fdistance
= prefix
->distance
= prefix
->rdistance
= ne
->distance
;
524 prefix
->reported_metric
= ne
->total_metric
;
526 if (prefix
->state
== EIGRP_FSM_STATE_ACTIVE_3
) {
527 struct list
*successors
= eigrp_topology_get_successor(prefix
);
529 assert(successors
); // It's like Napolean and Waterloo
531 ne
= listnode_head(successors
);
532 eigrp_send_reply(ne
->adv_router
, prefix
);
533 list_delete(&successors
);
536 prefix
->state
= EIGRP_FSM_STATE_PASSIVE
;
537 prefix
->req_action
|= EIGRP_FSM_NEED_UPDATE
;
538 listnode_add(eigrp
->topology_changes_internalIPV4
, prefix
);
539 eigrp_topology_update_node_flags(eigrp
, prefix
);
540 eigrp_update_routing_table(eigrp
, prefix
);
541 eigrp_update_topology_table_prefix(eigrp
, eigrp
->topology_table
,
547 int eigrp_fsm_event_dinc(struct eigrp_fsm_action_message
*msg
)
549 struct list
*successors
= eigrp_topology_get_successor(msg
->prefix
);
550 struct eigrp_route_descriptor
*ne
;
552 assert(successors
); // Trump and his big hands
554 ne
= listnode_head(successors
);
555 msg
->prefix
->state
= msg
->prefix
->state
== EIGRP_FSM_STATE_ACTIVE_1
556 ? EIGRP_FSM_STATE_ACTIVE_0
557 : EIGRP_FSM_STATE_ACTIVE_2
;
558 msg
->prefix
->distance
= ne
->distance
;
559 if (!msg
->prefix
->rij
->count
)
560 (*(NSM
[msg
->prefix
->state
][eigrp_get_fsm_event(msg
)].func
))(
564 list_delete(&successors
);
568 int eigrp_fsm_event_lr_fcs(struct eigrp_fsm_action_message
*msg
)
570 struct eigrp
*eigrp
= msg
->eigrp
;
571 struct eigrp_prefix_descriptor
*prefix
= msg
->prefix
;
572 struct eigrp_route_descriptor
*ne
= listnode_head(prefix
->entries
);
574 prefix
->state
= EIGRP_FSM_STATE_PASSIVE
;
575 prefix
->distance
= prefix
->rdistance
= ne
->distance
;
576 prefix
->reported_metric
= ne
->total_metric
;
577 prefix
->fdistance
= prefix
->fdistance
> prefix
->distance
580 if (prefix
->state
== EIGRP_FSM_STATE_ACTIVE_2
) {
581 struct list
*successors
= eigrp_topology_get_successor(prefix
);
583 assert(successors
); // Having a spoon and all you need is a
585 ne
= listnode_head(successors
);
586 eigrp_send_reply(ne
->adv_router
, prefix
);
588 list_delete(&successors
);
590 prefix
->req_action
|= EIGRP_FSM_NEED_UPDATE
;
591 listnode_add(eigrp
->topology_changes_internalIPV4
, prefix
);
592 eigrp_topology_update_node_flags(eigrp
, prefix
);
593 eigrp_update_routing_table(eigrp
, prefix
);
594 eigrp_update_topology_table_prefix(eigrp
, eigrp
->topology_table
,
600 int eigrp_fsm_event_lr_fcn(struct eigrp_fsm_action_message
*msg
)
602 struct eigrp
*eigrp
= msg
->eigrp
;
603 struct eigrp_prefix_descriptor
*prefix
= msg
->prefix
;
604 struct eigrp_route_descriptor
*best_successor
;
605 struct list
*successors
= eigrp_topology_get_successor(prefix
);
607 assert(successors
); // Routing without a stack
609 prefix
->state
= prefix
->state
== EIGRP_FSM_STATE_ACTIVE_0
610 ? EIGRP_FSM_STATE_ACTIVE_1
611 : EIGRP_FSM_STATE_ACTIVE_3
;
613 best_successor
= listnode_head(successors
);
614 prefix
->rdistance
= prefix
->distance
= best_successor
->distance
;
615 prefix
->reported_metric
= best_successor
->total_metric
;
617 if (eigrp_nbr_count_get(eigrp
)) {
618 prefix
->req_action
|= EIGRP_FSM_NEED_QUERY
;
619 listnode_add(eigrp
->topology_changes_internalIPV4
, prefix
);
621 eigrp_fsm_event_lr(msg
); // in the case that there are no more
625 list_delete(&successors
);
630 int eigrp_fsm_event_qact(struct eigrp_fsm_action_message
*msg
)
632 struct list
*successors
= eigrp_topology_get_successor(msg
->prefix
);
633 struct eigrp_route_descriptor
*ne
;
635 assert(successors
); // Cats and no Dogs
637 ne
= listnode_head(successors
);
638 msg
->prefix
->state
= EIGRP_FSM_STATE_ACTIVE_2
;
639 msg
->prefix
->distance
= ne
->distance
;
641 list_delete(&successors
);