/src/frr/ospfd/ospf_nsm.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* |
3 | | * OSPF version 2 Neighbor State Machine |
4 | | * From RFC2328 [OSPF Version 2] |
5 | | * Copyright (C) 1999, 2000 Toshiaki Takada |
6 | | */ |
7 | | |
8 | | #include <zebra.h> |
9 | | |
10 | | #include "frrevent.h" |
11 | | #include "memory.h" |
12 | | #include "hash.h" |
13 | | #include "linklist.h" |
14 | | #include "prefix.h" |
15 | | #include "if.h" |
16 | | #include "table.h" |
17 | | #include "stream.h" |
18 | | #include "table.h" |
19 | | #include "log.h" |
20 | | #include "command.h" |
21 | | #include "network.h" |
22 | | |
23 | | #include "ospfd/ospfd.h" |
24 | | #include "ospfd/ospf_interface.h" |
25 | | #include "ospfd/ospf_ism.h" |
26 | | #include "ospfd/ospf_asbr.h" |
27 | | #include "ospfd/ospf_lsa.h" |
28 | | #include "ospfd/ospf_lsdb.h" |
29 | | #include "ospfd/ospf_neighbor.h" |
30 | | #include "ospfd/ospf_nsm.h" |
31 | | #include "ospfd/ospf_network.h" |
32 | | #include "ospfd/ospf_packet.h" |
33 | | #include "ospfd/ospf_dump.h" |
34 | | #include "ospfd/ospf_flood.h" |
35 | | #include "ospfd/ospf_abr.h" |
36 | | #include "ospfd/ospf_bfd.h" |
37 | | #include "ospfd/ospf_gr.h" |
38 | | #include "ospfd/ospf_errors.h" |
39 | | |
40 | | DEFINE_HOOK(ospf_nsm_change, |
41 | | (struct ospf_neighbor * on, int state, int oldstate), |
42 | | (on, state, oldstate)); |
43 | | |
44 | | static void nsm_clear_adj(struct ospf_neighbor *); |
45 | | |
46 | | /* OSPF NSM Timer functions. */ |
47 | | static void ospf_inactivity_timer(struct event *thread) |
48 | 0 | { |
49 | 0 | struct ospf_neighbor *nbr; |
50 | 0 |
|
51 | 0 | nbr = EVENT_ARG(thread); |
52 | 0 | nbr->t_inactivity = NULL; |
53 | 0 |
|
54 | 0 | if (IS_DEBUG_OSPF(nsm, NSM_TIMERS)) |
55 | 0 | zlog_debug("NSM[%s:%pI4:%s]: Timer (Inactivity timer expire)", |
56 | 0 | IF_NAME(nbr->oi), &nbr->router_id, |
57 | 0 | ospf_get_name(nbr->oi->ospf)); |
58 | 0 |
|
59 | 0 | /* Dont trigger NSM_InactivityTimer event , if the current |
60 | 0 | * router acting as HELPER for this neighbour. |
61 | 0 | */ |
62 | 0 | if (!OSPF_GR_IS_ACTIVE_HELPER(nbr)) |
63 | 0 | OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_InactivityTimer); |
64 | 0 | else { |
65 | 0 | if (IS_DEBUG_OSPF_GR) |
66 | 0 | zlog_debug( |
67 | 0 | "%s, Acting as HELPER for this neighbour, So restart the dead timer", |
68 | 0 | __func__); |
69 | 0 | OSPF_NSM_TIMER_ON(nbr->t_inactivity, ospf_inactivity_timer, |
70 | 0 | nbr->v_inactivity); |
71 | 0 | } |
72 | 0 | } |
73 | | |
74 | | static void ospf_db_desc_timer(struct event *thread) |
75 | 0 | { |
76 | 0 | struct ospf_neighbor *nbr; |
77 | 0 |
|
78 | 0 | nbr = EVENT_ARG(thread); |
79 | 0 | nbr->t_db_desc = NULL; |
80 | 0 |
|
81 | 0 | if (IS_DEBUG_OSPF(nsm, NSM_TIMERS)) |
82 | 0 | zlog_debug("NSM[%s:%pI4:%s]: Timer (DD Retransmit timer expire)", |
83 | 0 | IF_NAME(nbr->oi), &nbr->src, |
84 | 0 | ospf_get_name(nbr->oi->ospf)); |
85 | 0 |
|
86 | 0 | /* resent last send DD packet. */ |
87 | 0 | assert(nbr->last_send); |
88 | 0 | ospf_db_desc_resend(nbr); |
89 | 0 |
|
90 | 0 | /* DD Retransmit timer set. */ |
91 | 0 | OSPF_NSM_TIMER_ON(nbr->t_db_desc, ospf_db_desc_timer, nbr->v_db_desc); |
92 | 0 | } |
93 | | |
94 | | /* Hook function called after ospf NSM event is occurred. |
95 | | * |
96 | | * Set/clear any timers whose condition is implicit to the neighbour |
97 | | * state. There may be other timers which are set/unset according to other |
98 | | * state. |
99 | | * |
100 | | * We rely on this function to properly clear timers in lower states, |
101 | | * particularly before deleting a neighbour. |
102 | | */ |
103 | | static void nsm_timer_set(struct ospf_neighbor *nbr) |
104 | 0 | { |
105 | 0 | switch (nbr->state) { |
106 | 0 | case NSM_Deleted: |
107 | 0 | case NSM_Down: |
108 | 0 | EVENT_OFF(nbr->t_inactivity); |
109 | 0 | EVENT_OFF(nbr->t_hello_reply); |
110 | | /* fallthru */ |
111 | 0 | case NSM_Attempt: |
112 | 0 | case NSM_Init: |
113 | 0 | case NSM_TwoWay: |
114 | 0 | EVENT_OFF(nbr->t_db_desc); |
115 | 0 | EVENT_OFF(nbr->t_ls_upd); |
116 | 0 | EVENT_OFF(nbr->t_ls_req); |
117 | 0 | break; |
118 | 0 | case NSM_ExStart: |
119 | 0 | OSPF_NSM_TIMER_ON(nbr->t_db_desc, ospf_db_desc_timer, |
120 | 0 | nbr->v_db_desc); |
121 | 0 | EVENT_OFF(nbr->t_ls_upd); |
122 | 0 | EVENT_OFF(nbr->t_ls_req); |
123 | 0 | break; |
124 | 0 | case NSM_Exchange: |
125 | 0 | OSPF_NSM_TIMER_ON(nbr->t_ls_upd, ospf_ls_upd_timer, |
126 | 0 | nbr->v_ls_upd); |
127 | 0 | if (!IS_SET_DD_MS(nbr->dd_flags)) |
128 | 0 | EVENT_OFF(nbr->t_db_desc); |
129 | 0 | break; |
130 | 0 | case NSM_Loading: |
131 | 0 | case NSM_Full: |
132 | 0 | default: |
133 | 0 | EVENT_OFF(nbr->t_db_desc); |
134 | 0 | break; |
135 | 0 | } |
136 | 0 | } |
137 | | |
138 | | /* 10.4 of RFC2328, indicate whether an adjacency is appropriate with |
139 | | * the given neighbour |
140 | | */ |
141 | | int nsm_should_adj(struct ospf_neighbor *nbr) |
142 | 0 | { |
143 | 0 | struct ospf_interface *oi = nbr->oi; |
144 | | |
145 | | /* These network types must always form adjacencies. */ |
146 | 0 | if (oi->type == OSPF_IFTYPE_POINTOPOINT |
147 | 0 | || oi->type == OSPF_IFTYPE_POINTOMULTIPOINT |
148 | 0 | || oi->type == OSPF_IFTYPE_VIRTUALLINK |
149 | | /* Router itself is the DRouter or the BDRouter. */ |
150 | 0 | || IPV4_ADDR_SAME(&oi->address->u.prefix4, &DR(oi)) |
151 | 0 | || IPV4_ADDR_SAME(&oi->address->u.prefix4, &BDR(oi)) |
152 | | /* Neighboring Router is the DRouter or the BDRouter. */ |
153 | 0 | || IPV4_ADDR_SAME(&nbr->address.u.prefix4, &DR(oi)) |
154 | 0 | || IPV4_ADDR_SAME(&nbr->address.u.prefix4, &BDR(oi))) |
155 | 0 | return 1; |
156 | | |
157 | 0 | return 0; |
158 | 0 | } |
159 | | |
160 | | /* OSPF NSM functions. */ |
161 | | static int nsm_hello_received(struct ospf_neighbor *nbr) |
162 | 0 | { |
163 | | /* Start or Restart Inactivity Timer. */ |
164 | 0 | EVENT_OFF(nbr->t_inactivity); |
165 | |
|
166 | 0 | OSPF_NSM_TIMER_ON(nbr->t_inactivity, ospf_inactivity_timer, |
167 | 0 | nbr->v_inactivity); |
168 | |
|
169 | 0 | if (nbr->oi->type == OSPF_IFTYPE_NBMA && nbr->nbr_nbma) |
170 | 0 | EVENT_OFF(nbr->nbr_nbma->t_poll); |
171 | | |
172 | | /* Send proactive ARP requests */ |
173 | 0 | if (nbr->state < NSM_Exchange) |
174 | 0 | ospf_proactively_arp(nbr); |
175 | |
|
176 | 0 | return 0; |
177 | 0 | } |
178 | | |
179 | | static int nsm_start(struct ospf_neighbor *nbr) |
180 | 0 | { |
181 | 0 | if (nbr->nbr_nbma) |
182 | 0 | EVENT_OFF(nbr->nbr_nbma->t_poll); |
183 | |
|
184 | 0 | EVENT_OFF(nbr->t_inactivity); |
185 | |
|
186 | 0 | OSPF_NSM_TIMER_ON(nbr->t_inactivity, ospf_inactivity_timer, |
187 | 0 | nbr->v_inactivity); |
188 | | |
189 | | /* Send proactive ARP requests */ |
190 | 0 | ospf_proactively_arp(nbr); |
191 | |
|
192 | 0 | return 0; |
193 | 0 | } |
194 | | |
195 | | static int nsm_twoway_received(struct ospf_neighbor *nbr) |
196 | 0 | { |
197 | 0 | int adj = nsm_should_adj(nbr); |
198 | | |
199 | | /* Send proactive ARP requests */ |
200 | 0 | if (adj) |
201 | 0 | ospf_proactively_arp(nbr); |
202 | |
|
203 | 0 | return (adj ? NSM_ExStart : NSM_TwoWay); |
204 | 0 | } |
205 | | |
206 | | int ospf_db_summary_count(struct ospf_neighbor *nbr) |
207 | 0 | { |
208 | 0 | return ospf_lsdb_count_all(&nbr->db_sum); |
209 | 0 | } |
210 | | |
211 | | int ospf_db_summary_isempty(struct ospf_neighbor *nbr) |
212 | 59 | { |
213 | 59 | return ospf_lsdb_isempty(&nbr->db_sum); |
214 | 59 | } |
215 | | |
216 | | static int ospf_db_summary_add(struct ospf_neighbor *nbr, struct ospf_lsa *lsa) |
217 | 0 | { |
218 | 0 | switch (lsa->data->type) { |
219 | 0 | case OSPF_OPAQUE_LINK_LSA: |
220 | | /* Exclude type-9 LSAs that does not have the same "oi" with |
221 | | * "nbr". */ |
222 | 0 | if (ospf_if_exists(lsa->oi) != nbr->oi) |
223 | 0 | return 0; |
224 | 0 | break; |
225 | 0 | case OSPF_OPAQUE_AREA_LSA: |
226 | | /* |
227 | | * It is assured by the caller function "nsm_negotiation_done()" |
228 | | * that every given LSA belongs to the same area with "nbr". |
229 | | */ |
230 | 0 | break; |
231 | 0 | case OSPF_OPAQUE_AS_LSA: |
232 | 0 | default: |
233 | 0 | break; |
234 | 0 | } |
235 | | |
236 | | /* Stay away from any Local Translated Type-7 LSAs */ |
237 | 0 | if (CHECK_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT)) |
238 | 0 | return 0; |
239 | | |
240 | 0 | if (IS_LSA_MAXAGE(lsa)) |
241 | 0 | ospf_ls_retransmit_add(nbr, lsa); |
242 | 0 | else |
243 | 0 | ospf_lsdb_add(&nbr->db_sum, lsa); |
244 | |
|
245 | 0 | return 0; |
246 | 0 | } |
247 | | |
248 | | void ospf_db_summary_clear(struct ospf_neighbor *nbr) |
249 | 0 | { |
250 | 0 | struct ospf_lsdb *lsdb; |
251 | 0 | int i; |
252 | |
|
253 | 0 | lsdb = &nbr->db_sum; |
254 | 0 | for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) { |
255 | 0 | struct route_table *table = lsdb->type[i].db; |
256 | 0 | struct route_node *rn; |
257 | |
|
258 | 0 | for (rn = route_top(table); rn; rn = route_next(rn)) |
259 | 0 | if (rn->info) |
260 | 0 | ospf_lsdb_delete(&nbr->db_sum, rn->info); |
261 | 0 | } |
262 | 0 | } |
263 | | |
264 | | |
265 | | /* The area link state database consists of the router-LSAs, |
266 | | network-LSAs and summary-LSAs contained in the area structure, |
267 | | along with the AS-external-LSAs contained in the global structure. |
268 | | AS-external-LSAs are omitted from a virtual neighbor's Database |
269 | | summary list. AS-external-LSAs are omitted from the Database |
270 | | summary list if the area has been configured as a stub. */ |
271 | | static int nsm_negotiation_done(struct ospf_neighbor *nbr) |
272 | 0 | { |
273 | 0 | struct ospf_area *area = nbr->oi->area; |
274 | 0 | struct ospf_lsa *lsa; |
275 | 0 | struct route_node *rn; |
276 | | |
277 | | /* Send proactive ARP requests */ |
278 | 0 | ospf_proactively_arp(nbr); |
279 | |
|
280 | 0 | LSDB_LOOP (ROUTER_LSDB(area), rn, lsa) |
281 | 0 | ospf_db_summary_add(nbr, lsa); |
282 | 0 | LSDB_LOOP (NETWORK_LSDB(area), rn, lsa) |
283 | 0 | ospf_db_summary_add(nbr, lsa); |
284 | 0 | LSDB_LOOP (SUMMARY_LSDB(area), rn, lsa) |
285 | 0 | ospf_db_summary_add(nbr, lsa); |
286 | | |
287 | | /* Process only if the neighbor is opaque capable. */ |
288 | 0 | if (CHECK_FLAG(nbr->options, OSPF_OPTION_O)) { |
289 | 0 | LSDB_LOOP (OPAQUE_LINK_LSDB(area), rn, lsa) |
290 | 0 | ospf_db_summary_add(nbr, lsa); |
291 | 0 | LSDB_LOOP (OPAQUE_AREA_LSDB(area), rn, lsa) |
292 | 0 | ospf_db_summary_add(nbr, lsa); |
293 | 0 | } |
294 | |
|
295 | 0 | if (CHECK_FLAG(nbr->options, OSPF_OPTION_NP)) { |
296 | 0 | LSDB_LOOP (NSSA_LSDB(area), rn, lsa) |
297 | 0 | ospf_db_summary_add(nbr, lsa); |
298 | 0 | } |
299 | | |
300 | | /* For Stub/NSSA area, we should not send Type-4 and Type-5 LSAs */ |
301 | 0 | if (nbr->oi->type != OSPF_IFTYPE_VIRTUALLINK |
302 | 0 | && area->external_routing == OSPF_AREA_DEFAULT) { |
303 | 0 | LSDB_LOOP (ASBR_SUMMARY_LSDB(area), rn, lsa) |
304 | 0 | ospf_db_summary_add(nbr, lsa); |
305 | 0 | LSDB_LOOP (EXTERNAL_LSDB(nbr->oi->ospf), rn, lsa) |
306 | 0 | ospf_db_summary_add(nbr, lsa); |
307 | 0 | } |
308 | |
|
309 | 0 | if (CHECK_FLAG(nbr->options, OSPF_OPTION_O) |
310 | 0 | && (nbr->oi->type != OSPF_IFTYPE_VIRTUALLINK |
311 | 0 | && area->external_routing == OSPF_AREA_DEFAULT)) |
312 | 0 | LSDB_LOOP (OPAQUE_AS_LSDB(nbr->oi->ospf), rn, lsa) |
313 | 0 | ospf_db_summary_add(nbr, lsa); |
314 | |
|
315 | 0 | return 0; |
316 | 0 | } |
317 | | |
318 | | static int nsm_exchange_done(struct ospf_neighbor *nbr) |
319 | 0 | { |
320 | 0 | if (ospf_ls_request_isempty(nbr)) |
321 | 0 | return NSM_Full; |
322 | | |
323 | | /* Send Link State Request. */ |
324 | 0 | if (nbr->t_ls_req == NULL) |
325 | 0 | ospf_ls_req_send(nbr); |
326 | |
|
327 | 0 | return NSM_Loading; |
328 | 0 | } |
329 | | |
330 | | static int nsm_adj_ok(struct ospf_neighbor *nbr) |
331 | 0 | { |
332 | 0 | int next_state = nbr->state; |
333 | 0 | int adj = nsm_should_adj(nbr); |
334 | |
|
335 | 0 | if (nbr->state == NSM_TwoWay && adj == 1) { |
336 | 0 | next_state = NSM_ExStart; |
337 | | |
338 | | /* Send proactive ARP requests */ |
339 | 0 | ospf_proactively_arp(nbr); |
340 | 0 | } else if (nbr->state >= NSM_ExStart && adj == 0) |
341 | 0 | next_state = NSM_TwoWay; |
342 | |
|
343 | 0 | return next_state; |
344 | 0 | } |
345 | | |
346 | | /* Clear adjacency related state for a neighbour, intended where nbr |
347 | | * transitions from > ExStart (i.e. a Full or forming adjacency) |
348 | | * to <= ExStart. |
349 | | */ |
350 | | static void nsm_clear_adj(struct ospf_neighbor *nbr) |
351 | 0 | { |
352 | | /* Clear Database Summary list. */ |
353 | 0 | if (!ospf_db_summary_isempty(nbr)) |
354 | 0 | ospf_db_summary_clear(nbr); |
355 | | |
356 | | /* Clear Link State Request list. */ |
357 | 0 | if (!ospf_ls_request_isempty(nbr)) |
358 | 0 | ospf_ls_request_delete_all(nbr); |
359 | | |
360 | | /* Clear Link State Retransmission list. */ |
361 | 0 | if (!ospf_ls_retransmit_isempty(nbr)) |
362 | 0 | ospf_ls_retransmit_clear(nbr); |
363 | |
|
364 | 0 | if (CHECK_FLAG(nbr->options, OSPF_OPTION_O)) |
365 | 0 | UNSET_FLAG(nbr->options, OSPF_OPTION_O); |
366 | 0 | } |
367 | | |
368 | | static int nsm_kill_nbr(struct ospf_neighbor *nbr) |
369 | 0 | { |
370 | 0 | struct ospf_interface *oi = nbr->oi; |
371 | 0 | struct ospf_neighbor *on; |
372 | 0 | struct route_node *rn; |
373 | | |
374 | | /* killing nbr_self is invalid */ |
375 | 0 | if (nbr == nbr->oi->nbr_self) { |
376 | 0 | assert(nbr != nbr->oi->nbr_self); |
377 | 0 | return 0; |
378 | 0 | } |
379 | | |
380 | 0 | if (nbr->oi->type == OSPF_IFTYPE_NBMA && nbr->nbr_nbma != NULL) { |
381 | 0 | struct ospf_nbr_nbma *nbr_nbma = nbr->nbr_nbma; |
382 | |
|
383 | 0 | nbr_nbma->nbr = NULL; |
384 | 0 | nbr_nbma->state_change = nbr->state_change; |
385 | |
|
386 | 0 | nbr->nbr_nbma = NULL; |
387 | |
|
388 | 0 | OSPF_POLL_TIMER_ON(nbr_nbma->t_poll, ospf_poll_timer, |
389 | 0 | nbr_nbma->v_poll); |
390 | |
|
391 | 0 | if (IS_DEBUG_OSPF(nsm, NSM_EVENTS)) |
392 | 0 | zlog_debug( |
393 | 0 | "NSM[%s:%pI4:%s]: Down (PollIntervalTimer scheduled)", |
394 | 0 | IF_NAME(nbr->oi), |
395 | 0 | &nbr->address.u.prefix4, |
396 | 0 | ospf_get_name(nbr->oi->ospf)); |
397 | 0 | } |
398 | | |
399 | | /* |
400 | | * Do we have any neighbors that are also operating |
401 | | * on this interface? |
402 | | */ |
403 | 0 | for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { |
404 | 0 | on = rn->info; |
405 | |
|
406 | 0 | if (!on) |
407 | 0 | continue; |
408 | | |
409 | 0 | if (on == nbr || on == oi->nbr_self) |
410 | 0 | continue; |
411 | | |
412 | | /* |
413 | | * on is in some state where we might be |
414 | | * sending packets on this interface |
415 | | */ |
416 | 0 | if (on->state > NSM_Down) { |
417 | 0 | route_unlock_node(rn); |
418 | 0 | return 0; |
419 | 0 | } |
420 | 0 | } |
421 | | /* |
422 | | * If we get here we know that this interface |
423 | | * has no neighbors in a state where we could |
424 | | * be sending packets. Let's flush anything |
425 | | * we got. |
426 | | */ |
427 | 0 | ospf_interface_fifo_flush(oi); |
428 | 0 | return 0; |
429 | 0 | } |
430 | | |
431 | | /* Neighbor State Machine */ |
432 | | const struct { |
433 | | int (*func)(struct ospf_neighbor *); |
434 | | int next_state; |
435 | | } NSM[OSPF_NSM_STATE_MAX][OSPF_NSM_EVENT_MAX] = { |
436 | | { |
437 | | /* DependUpon: dummy state. */ |
438 | | {NULL, NSM_DependUpon}, /* NoEvent */ |
439 | | {NULL, NSM_DependUpon}, /* HelloReceived */ |
440 | | {NULL, NSM_DependUpon}, /* Start */ |
441 | | {NULL, NSM_DependUpon}, /* 2-WayReceived */ |
442 | | {NULL, NSM_DependUpon}, /* NegotiationDone */ |
443 | | {NULL, NSM_DependUpon}, /* ExchangeDone */ |
444 | | {NULL, NSM_DependUpon}, /* BadLSReq */ |
445 | | {NULL, NSM_DependUpon}, /* LoadingDone */ |
446 | | {NULL, NSM_DependUpon}, /* AdjOK? */ |
447 | | {NULL, NSM_DependUpon}, /* SeqNumberMismatch */ |
448 | | {NULL, NSM_DependUpon}, /* 1-WayReceived */ |
449 | | {NULL, NSM_DependUpon}, /* KillNbr */ |
450 | | {NULL, NSM_DependUpon}, /* InactivityTimer */ |
451 | | {NULL, NSM_DependUpon}, /* LLDown */ |
452 | | }, |
453 | | { |
454 | | /* Deleted: dummy state. */ |
455 | | {NULL, NSM_Deleted}, /* NoEvent */ |
456 | | {NULL, NSM_Deleted}, /* HelloReceived */ |
457 | | {NULL, NSM_Deleted}, /* Start */ |
458 | | {NULL, NSM_Deleted}, /* 2-WayReceived */ |
459 | | {NULL, NSM_Deleted}, /* NegotiationDone */ |
460 | | {NULL, NSM_Deleted}, /* ExchangeDone */ |
461 | | {NULL, NSM_Deleted}, /* BadLSReq */ |
462 | | {NULL, NSM_Deleted}, /* LoadingDone */ |
463 | | {NULL, NSM_Deleted}, /* AdjOK? */ |
464 | | {NULL, NSM_Deleted}, /* SeqNumberMismatch */ |
465 | | {NULL, NSM_Deleted}, /* 1-WayReceived */ |
466 | | {NULL, NSM_Deleted}, /* KillNbr */ |
467 | | {NULL, NSM_Deleted}, /* InactivityTimer */ |
468 | | {NULL, NSM_Deleted}, /* LLDown */ |
469 | | }, |
470 | | { |
471 | | /* Down: */ |
472 | | {NULL, NSM_DependUpon}, /* NoEvent */ |
473 | | {nsm_hello_received, NSM_Init}, /* HelloReceived */ |
474 | | {nsm_start, NSM_Attempt}, /* Start */ |
475 | | {NULL, NSM_Down}, /* 2-WayReceived */ |
476 | | {NULL, NSM_Down}, /* NegotiationDone */ |
477 | | {NULL, NSM_Down}, /* ExchangeDone */ |
478 | | {NULL, NSM_Down}, /* BadLSReq */ |
479 | | {NULL, NSM_Down}, /* LoadingDone */ |
480 | | {NULL, NSM_Down}, /* AdjOK? */ |
481 | | {NULL, NSM_Down}, /* SeqNumberMismatch */ |
482 | | {NULL, NSM_Down}, /* 1-WayReceived */ |
483 | | {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */ |
484 | | {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */ |
485 | | {nsm_kill_nbr, NSM_Deleted}, /* LLDown */ |
486 | | }, |
487 | | { |
488 | | /* Attempt: */ |
489 | | {NULL, NSM_DependUpon}, /* NoEvent */ |
490 | | {nsm_hello_received, NSM_Init}, /* HelloReceived */ |
491 | | {NULL, NSM_Attempt}, /* Start */ |
492 | | {NULL, NSM_Attempt}, /* 2-WayReceived */ |
493 | | {NULL, NSM_Attempt}, /* NegotiationDone */ |
494 | | {NULL, NSM_Attempt}, /* ExchangeDone */ |
495 | | {NULL, NSM_Attempt}, /* BadLSReq */ |
496 | | {NULL, NSM_Attempt}, /* LoadingDone */ |
497 | | {NULL, NSM_Attempt}, /* AdjOK? */ |
498 | | {NULL, NSM_Attempt}, /* SeqNumberMismatch */ |
499 | | {NULL, NSM_Attempt}, /* 1-WayReceived */ |
500 | | {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */ |
501 | | {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */ |
502 | | {nsm_kill_nbr, NSM_Deleted}, /* LLDown */ |
503 | | }, |
504 | | { |
505 | | /* Init: */ |
506 | | {NULL, NSM_DependUpon}, /* NoEvent */ |
507 | | {nsm_hello_received, NSM_Init}, /* HelloReceived */ |
508 | | {NULL, NSM_Init}, /* Start */ |
509 | | {nsm_twoway_received, NSM_DependUpon}, /* 2-WayReceived */ |
510 | | {NULL, NSM_Init}, /* NegotiationDone */ |
511 | | {NULL, NSM_Init}, /* ExchangeDone */ |
512 | | {NULL, NSM_Init}, /* BadLSReq */ |
513 | | {NULL, NSM_Init}, /* LoadingDone */ |
514 | | {NULL, NSM_Init}, /* AdjOK? */ |
515 | | {NULL, NSM_Init}, /* SeqNumberMismatch */ |
516 | | {NULL, NSM_Init}, /* 1-WayReceived */ |
517 | | {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */ |
518 | | {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */ |
519 | | {nsm_kill_nbr, NSM_Deleted}, /* LLDown */ |
520 | | }, |
521 | | { |
522 | | /* 2-Way: */ |
523 | | {NULL, NSM_DependUpon}, /* NoEvent */ |
524 | | {nsm_hello_received, NSM_TwoWay}, /* HelloReceived */ |
525 | | {NULL, NSM_TwoWay}, /* Start */ |
526 | | {NULL, NSM_TwoWay}, /* 2-WayReceived */ |
527 | | {NULL, NSM_TwoWay}, /* NegotiationDone */ |
528 | | {NULL, NSM_TwoWay}, /* ExchangeDone */ |
529 | | {NULL, NSM_TwoWay}, /* BadLSReq */ |
530 | | {NULL, NSM_TwoWay}, /* LoadingDone */ |
531 | | {nsm_adj_ok, NSM_DependUpon}, /* AdjOK? */ |
532 | | {NULL, NSM_TwoWay}, /* SeqNumberMismatch */ |
533 | | {NULL, NSM_Init}, /* 1-WayReceived */ |
534 | | {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */ |
535 | | {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */ |
536 | | {nsm_kill_nbr, NSM_Deleted}, /* LLDown */ |
537 | | }, |
538 | | { |
539 | | /* ExStart: */ |
540 | | {NULL, NSM_DependUpon}, /* NoEvent */ |
541 | | {nsm_hello_received, NSM_ExStart}, /* HelloReceived */ |
542 | | {NULL, NSM_ExStart}, /* Start */ |
543 | | {NULL, NSM_ExStart}, /* 2-WayReceived */ |
544 | | {nsm_negotiation_done, NSM_Exchange}, /* NegotiationDone */ |
545 | | {NULL, NSM_ExStart}, /* ExchangeDone */ |
546 | | {NULL, NSM_ExStart}, /* BadLSReq */ |
547 | | {NULL, NSM_ExStart}, /* LoadingDone */ |
548 | | {nsm_adj_ok, NSM_DependUpon}, /* AdjOK? */ |
549 | | {NULL, NSM_ExStart}, /* SeqNumberMismatch */ |
550 | | {NULL, NSM_Init}, /* 1-WayReceived */ |
551 | | {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */ |
552 | | {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */ |
553 | | {nsm_kill_nbr, NSM_Deleted}, /* LLDown */ |
554 | | }, |
555 | | { |
556 | | /* Exchange: */ |
557 | | {NULL, NSM_DependUpon}, /* NoEvent */ |
558 | | {nsm_hello_received, NSM_Exchange}, /* HelloReceived */ |
559 | | {NULL, NSM_Exchange}, /* Start */ |
560 | | {NULL, NSM_Exchange}, /* 2-WayReceived */ |
561 | | {NULL, NSM_Exchange}, /* NegotiationDone */ |
562 | | {nsm_exchange_done, NSM_DependUpon}, /* ExchangeDone */ |
563 | | {NULL, NSM_ExStart}, /* BadLSReq */ |
564 | | {NULL, NSM_Exchange}, /* LoadingDone */ |
565 | | {nsm_adj_ok, NSM_DependUpon}, /* AdjOK? */ |
566 | | {NULL, NSM_ExStart}, /* SeqNumberMismatch */ |
567 | | {NULL, NSM_Init}, /* 1-WayReceived */ |
568 | | {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */ |
569 | | {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */ |
570 | | {nsm_kill_nbr, NSM_Deleted}, /* LLDown */ |
571 | | }, |
572 | | { |
573 | | /* Loading: */ |
574 | | {NULL, NSM_DependUpon}, /* NoEvent */ |
575 | | {nsm_hello_received, NSM_Loading}, /* HelloReceived */ |
576 | | {NULL, NSM_Loading}, /* Start */ |
577 | | {NULL, NSM_Loading}, /* 2-WayReceived */ |
578 | | {NULL, NSM_Loading}, /* NegotiationDone */ |
579 | | {NULL, NSM_Loading}, /* ExchangeDone */ |
580 | | {NULL, NSM_ExStart}, /* BadLSReq */ |
581 | | {NULL, NSM_Full}, /* LoadingDone */ |
582 | | {nsm_adj_ok, NSM_DependUpon}, /* AdjOK? */ |
583 | | {NULL, NSM_ExStart}, /* SeqNumberMismatch */ |
584 | | {NULL, NSM_Init}, /* 1-WayReceived */ |
585 | | {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */ |
586 | | {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */ |
587 | | {nsm_kill_nbr, NSM_Deleted}, /* LLDown */ |
588 | | }, |
589 | | { |
590 | | /* Full: */ |
591 | | {NULL, NSM_DependUpon}, /* NoEvent */ |
592 | | {nsm_hello_received, NSM_Full}, /* HelloReceived */ |
593 | | {NULL, NSM_Full}, /* Start */ |
594 | | {NULL, NSM_Full}, /* 2-WayReceived */ |
595 | | {NULL, NSM_Full}, /* NegotiationDone */ |
596 | | {NULL, NSM_Full}, /* ExchangeDone */ |
597 | | {NULL, NSM_ExStart}, /* BadLSReq */ |
598 | | {NULL, NSM_Full}, /* LoadingDone */ |
599 | | {nsm_adj_ok, NSM_DependUpon}, /* AdjOK? */ |
600 | | {NULL, NSM_ExStart}, /* SeqNumberMismatch */ |
601 | | {NULL, NSM_Init}, /* 1-WayReceived */ |
602 | | {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */ |
603 | | {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */ |
604 | | {nsm_kill_nbr, NSM_Deleted}, /* LLDown */ |
605 | | }, |
606 | | }; |
607 | | |
608 | | static const char *const ospf_nsm_event_str[] = { |
609 | | "NoEvent", "HelloReceived", "Start", |
610 | | "2-WayReceived", "NegotiationDone", "ExchangeDone", |
611 | | "BadLSReq", "LoadingDone", "AdjOK?", |
612 | | "SeqNumberMismatch", "1-WayReceived", "KillNbr", |
613 | | "InactivityTimer", "LLDown", |
614 | | }; |
615 | | |
616 | | static void nsm_notice_state_change(struct ospf_neighbor *nbr, int next_state, |
617 | | int event) |
618 | 0 | { |
619 | | /* Logging change of status. */ |
620 | 0 | if (IS_DEBUG_OSPF(nsm, NSM_STATUS)) |
621 | 0 | zlog_debug("NSM[%s:%pI4:%s]: State change %s -> %s (%s)", |
622 | 0 | IF_NAME(nbr->oi), &nbr->router_id, |
623 | 0 | ospf_get_name(nbr->oi->ospf), |
624 | 0 | lookup_msg(ospf_nsm_state_msg, nbr->state, NULL), |
625 | 0 | lookup_msg(ospf_nsm_state_msg, next_state, NULL), |
626 | 0 | ospf_nsm_event_str[event]); |
627 | | |
628 | | /* Optionally notify about adjacency changes */ |
629 | 0 | if (CHECK_FLAG(nbr->oi->ospf->config, OSPF_LOG_ADJACENCY_CHANGES) |
630 | 0 | && (CHECK_FLAG(nbr->oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL) |
631 | 0 | || (next_state == NSM_Full) || (next_state < nbr->state))) |
632 | 0 | zlog_notice( |
633 | 0 | "AdjChg: Nbr %pI4, NbrIP %pI4 (%s) on %s: %s -> %s (%s)", |
634 | 0 | &nbr->router_id, &nbr->src, |
635 | 0 | ospf_get_name(nbr->oi->ospf), IF_NAME(nbr->oi), |
636 | 0 | lookup_msg(ospf_nsm_state_msg, nbr->state, NULL), |
637 | 0 | lookup_msg(ospf_nsm_state_msg, next_state, NULL), |
638 | 0 | ospf_nsm_event_str[event]); |
639 | | |
640 | | /* Advance in NSM */ |
641 | 0 | if (next_state > nbr->state) |
642 | 0 | monotime(&nbr->ts_last_progress); |
643 | 0 | else /* regression in NSM */ |
644 | 0 | { |
645 | 0 | monotime(&nbr->ts_last_regress); |
646 | 0 | nbr->last_regress_str = ospf_nsm_event_str[event]; |
647 | 0 | } |
648 | 0 | } |
649 | | |
650 | | static void nsm_change_state(struct ospf_neighbor *nbr, int state) |
651 | 0 | { |
652 | 0 | struct ospf_interface *oi = nbr->oi; |
653 | 0 | struct ospf_area *vl_area = NULL; |
654 | 0 | uint8_t old_state; |
655 | | |
656 | | /* Preserve old status. */ |
657 | 0 | old_state = nbr->state; |
658 | | |
659 | | /* Change to new status. */ |
660 | 0 | nbr->state = state; |
661 | | |
662 | | /* Statistics. */ |
663 | 0 | nbr->state_change++; |
664 | |
|
665 | 0 | if (oi->type == OSPF_IFTYPE_VIRTUALLINK) |
666 | 0 | vl_area = ospf_area_lookup_by_area_id(oi->ospf, |
667 | 0 | oi->vl_data->vl_area_id); |
668 | | |
669 | | /* Generate NeighborChange ISM event. |
670 | | * |
671 | | * In response to NeighborChange, DR election is rerun. The information |
672 | | * from the election process is required by the router-lsa construction. |
673 | | * |
674 | | * Therefore, trigger the event prior to refreshing the LSAs. */ |
675 | 0 | switch (oi->state) { |
676 | 0 | case ISM_DROther: |
677 | 0 | case ISM_Backup: |
678 | 0 | case ISM_DR: |
679 | 0 | if ((old_state < NSM_TwoWay && state >= NSM_TwoWay) |
680 | 0 | || (old_state >= NSM_TwoWay && state < NSM_TwoWay)) |
681 | 0 | OSPF_ISM_EVENT_EXECUTE(oi, ISM_NeighborChange); |
682 | 0 | break; |
683 | 0 | default: |
684 | | /* ISM_PointToPoint -> ISM_Down, ISM_Loopback -> ISM_Down, etc. |
685 | | */ |
686 | 0 | break; |
687 | 0 | } |
688 | | |
689 | | /* One of the neighboring routers changes to/from the FULL state. */ |
690 | 0 | if ((old_state != NSM_Full && state == NSM_Full) |
691 | 0 | || (old_state == NSM_Full && state != NSM_Full)) { |
692 | 0 | if (state == NSM_Full) { |
693 | 0 | oi->full_nbrs++; |
694 | 0 | oi->area->full_nbrs++; |
695 | |
|
696 | 0 | ospf_check_abr_status(oi->ospf); |
697 | |
|
698 | 0 | if (oi->type == OSPF_IFTYPE_VIRTUALLINK && vl_area) |
699 | 0 | if (++vl_area->full_vls == 1) |
700 | 0 | ospf_schedule_abr_task(oi->ospf); |
701 | 0 | } else { |
702 | 0 | oi->full_nbrs--; |
703 | 0 | oi->area->full_nbrs--; |
704 | |
|
705 | 0 | ospf_check_abr_status(oi->ospf); |
706 | |
|
707 | 0 | if (oi->type == OSPF_IFTYPE_VIRTUALLINK && vl_area) |
708 | 0 | if (vl_area->full_vls > 0) |
709 | 0 | if (--vl_area->full_vls == 0) |
710 | 0 | ospf_schedule_abr_task( |
711 | 0 | oi->ospf); |
712 | 0 | } |
713 | |
|
714 | 0 | if (CHECK_FLAG(oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) |
715 | 0 | zlog_info( |
716 | 0 | "%s:[%pI4:%s], %s -> %s): scheduling new router-LSA origination", |
717 | 0 | __func__, &nbr->router_id, |
718 | 0 | ospf_get_name(oi->ospf), |
719 | 0 | lookup_msg(ospf_nsm_state_msg, old_state, NULL), |
720 | 0 | lookup_msg(ospf_nsm_state_msg, state, NULL)); |
721 | | |
722 | | /* Dont originate router LSA if the current |
723 | | * router is acting as a HELPER for this neighbour. |
724 | | */ |
725 | 0 | if (!OSPF_GR_IS_ACTIVE_HELPER(nbr)) |
726 | 0 | ospf_router_lsa_update_area(oi->area); |
727 | |
|
728 | 0 | if (oi->type == OSPF_IFTYPE_VIRTUALLINK) { |
729 | 0 | vl_area = ospf_area_lookup_by_area_id( |
730 | 0 | oi->ospf, oi->vl_data->vl_area_id); |
731 | |
|
732 | 0 | if (vl_area) |
733 | 0 | ospf_router_lsa_update_area(vl_area); |
734 | 0 | } |
735 | | |
736 | | /* Dont originate/flush network LSA if the current |
737 | | * router is acting as a HELPER for this neighbour. |
738 | | */ |
739 | 0 | if (!OSPF_GR_IS_ACTIVE_HELPER(nbr)) { |
740 | | /* Originate network-LSA. */ |
741 | 0 | if (oi->state == ISM_DR) { |
742 | 0 | if (oi->network_lsa_self |
743 | 0 | && oi->full_nbrs == 0) { |
744 | 0 | ospf_lsa_flush_area( |
745 | 0 | oi->network_lsa_self, oi->area); |
746 | 0 | ospf_lsa_unlock(&oi->network_lsa_self); |
747 | 0 | oi->network_lsa_self = NULL; |
748 | 0 | } else |
749 | 0 | ospf_network_lsa_update(oi); |
750 | 0 | } |
751 | 0 | } |
752 | |
|
753 | 0 | if (state == NSM_Full && oi->ospf->gr_info.restart_in_progress) |
754 | 0 | ospf_gr_check_adjs(oi->ospf); |
755 | 0 | } |
756 | |
|
757 | 0 | ospf_opaque_nsm_change(nbr, old_state); |
758 | | |
759 | | /* State changes from > ExStart to <= ExStart should clear any Exchange |
760 | | * or Full/LSA Update related lists and state. |
761 | | * Potential causal events: BadLSReq, SeqNumberMismatch, AdjOK? |
762 | | */ |
763 | 0 | if ((old_state > NSM_ExStart) && (state <= NSM_ExStart)) |
764 | 0 | nsm_clear_adj(nbr); |
765 | | |
766 | | /* Start DD exchange protocol */ |
767 | 0 | if (state == NSM_ExStart) { |
768 | 0 | if (nbr->dd_seqnum == 0) |
769 | 0 | nbr->dd_seqnum = (uint32_t)frr_weak_random(); |
770 | 0 | else |
771 | 0 | nbr->dd_seqnum++; |
772 | |
|
773 | 0 | nbr->dd_flags = |
774 | 0 | OSPF_DD_FLAG_I | OSPF_DD_FLAG_M | OSPF_DD_FLAG_MS; |
775 | 0 | if (CHECK_FLAG(oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) |
776 | 0 | zlog_info( |
777 | 0 | "%s: Initializing [DD]: %pI4 with seqnum:%x , flags:%x", |
778 | 0 | ospf_get_name(oi->ospf), &nbr->router_id, |
779 | 0 | nbr->dd_seqnum, nbr->dd_flags); |
780 | 0 | ospf_db_desc_send(nbr); |
781 | 0 | } |
782 | | |
783 | | /* clear cryptographic sequence number */ |
784 | 0 | if (state == NSM_Down) |
785 | 0 | nbr->crypt_seqnum = 0; |
786 | |
|
787 | 0 | if (nbr->bfd_session) |
788 | 0 | ospf_bfd_trigger_event(nbr, old_state, state); |
789 | | |
790 | | /* Preserve old status? */ |
791 | 0 | } |
792 | | |
793 | | /* Execute NSM event process. */ |
794 | | void ospf_nsm_event(struct event *thread) |
795 | 0 | { |
796 | 0 | int event; |
797 | 0 | int next_state; |
798 | 0 | struct ospf_neighbor *nbr; |
799 | |
|
800 | 0 | nbr = EVENT_ARG(thread); |
801 | 0 | event = EVENT_VAL(thread); |
802 | |
|
803 | 0 | if (IS_DEBUG_OSPF(nsm, NSM_EVENTS)) |
804 | 0 | zlog_debug("NSM[%s:%pI4:%s]: %s (%s)", IF_NAME(nbr->oi), |
805 | 0 | &nbr->router_id, |
806 | 0 | ospf_get_name(nbr->oi->ospf), |
807 | 0 | lookup_msg(ospf_nsm_state_msg, nbr->state, NULL), |
808 | 0 | ospf_nsm_event_str[event]); |
809 | |
|
810 | 0 | next_state = NSM[nbr->state][event].next_state; |
811 | | |
812 | | /* Call function. */ |
813 | 0 | if (NSM[nbr->state][event].func != NULL) { |
814 | 0 | int func_state = (*(NSM[nbr->state][event].func))(nbr); |
815 | |
|
816 | 0 | if (NSM[nbr->state][event].next_state == NSM_DependUpon) |
817 | 0 | next_state = func_state; |
818 | 0 | else if (func_state) { |
819 | | /* There's a mismatch between the FSM tables and what an |
820 | | * FSM |
821 | | * action/state-change function returned. State changes |
822 | | * which |
823 | | * do not have conditional/DependUpon next-states should |
824 | | * not |
825 | | * try set next_state. |
826 | | */ |
827 | 0 | flog_err( |
828 | 0 | EC_OSPF_FSM_INVALID_STATE, |
829 | 0 | "NSM[%s:%pI4:%s]: %s (%s): Warning: action tried to change next_state to %s", |
830 | 0 | IF_NAME(nbr->oi), &nbr->router_id, |
831 | 0 | ospf_get_name(nbr->oi->ospf), |
832 | 0 | lookup_msg(ospf_nsm_state_msg, nbr->state, |
833 | 0 | NULL), |
834 | 0 | ospf_nsm_event_str[event], |
835 | 0 | lookup_msg(ospf_nsm_state_msg, func_state, |
836 | 0 | NULL)); |
837 | 0 | } |
838 | 0 | } |
839 | |
|
840 | 0 | assert(next_state != NSM_DependUpon); |
841 | | |
842 | | /* If state is changed. */ |
843 | 0 | if (next_state != nbr->state) { |
844 | 0 | int old_state = nbr->state; |
845 | |
|
846 | 0 | nsm_notice_state_change(nbr, next_state, event); |
847 | 0 | nsm_change_state(nbr, next_state); |
848 | |
|
849 | 0 | hook_call(ospf_nsm_change, nbr, next_state, old_state); |
850 | 0 | } |
851 | | |
852 | | /* Make sure timer is set. */ |
853 | 0 | nsm_timer_set(nbr); |
854 | | |
855 | | /* When event is NSM_KillNbr, InactivityTimer or LLDown, the neighbor |
856 | | * is deleted. |
857 | | * |
858 | | * Rather than encode knowledge here of which events lead to NBR |
859 | | * delete, we take our cue from the NSM table, via the dummy |
860 | | * 'Deleted' neighbour state. |
861 | | */ |
862 | 0 | if (nbr->state == NSM_Deleted) |
863 | 0 | ospf_nbr_delete(nbr); |
864 | 0 | } |
865 | | |
866 | | /* Check loading state. */ |
867 | | void ospf_check_nbr_loading(struct ospf_neighbor *nbr) |
868 | 0 | { |
869 | 0 | if (nbr->state == NSM_Loading) { |
870 | 0 | if (ospf_ls_request_isempty(nbr)) |
871 | 0 | OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_LoadingDone); |
872 | 0 | else if (nbr->ls_req_last == NULL) |
873 | 0 | ospf_ls_req_event(nbr); |
874 | 0 | } |
875 | 0 | } |