/src/frr/ospfd/ospf_ism.c
Line | Count | Source (jump to first uncovered line) |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* |
3 | | * OSPF version 2 Interface 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 "linklist.h" |
12 | | #include "prefix.h" |
13 | | #include "if.h" |
14 | | #include "table.h" |
15 | | #include "log.h" |
16 | | |
17 | | #include "ospfd/ospfd.h" |
18 | | #include "ospfd/ospf_interface.h" |
19 | | #include "ospfd/ospf_ism.h" |
20 | | #include "ospfd/ospf_asbr.h" |
21 | | #include "ospfd/ospf_lsa.h" |
22 | | #include "ospfd/ospf_lsdb.h" |
23 | | #include "ospfd/ospf_neighbor.h" |
24 | | #include "ospfd/ospf_nsm.h" |
25 | | #include "ospfd/ospf_network.h" |
26 | | #include "ospfd/ospf_dump.h" |
27 | | #include "ospfd/ospf_packet.h" |
28 | | #include "ospfd/ospf_flood.h" |
29 | | #include "ospfd/ospf_abr.h" |
30 | | |
31 | | DEFINE_HOOK(ospf_ism_change, |
32 | | (struct ospf_interface * oi, int state, int oldstate), |
33 | | (oi, state, oldstate)); |
34 | | |
35 | | /* elect DR and BDR. Refer to RFC2319 section 9.4 */ |
36 | | static struct ospf_neighbor *ospf_dr_election_sub(struct list *routers) |
37 | 0 | { |
38 | 0 | struct listnode *node; |
39 | 0 | struct ospf_neighbor *nbr, *max = NULL; |
40 | | |
41 | | /* Choose highest router priority. |
42 | | In case of tie, choose highest Router ID. */ |
43 | 0 | for (ALL_LIST_ELEMENTS_RO(routers, node, nbr)) { |
44 | 0 | if (max == NULL) |
45 | 0 | max = nbr; |
46 | 0 | else { |
47 | 0 | if (max->priority < nbr->priority) |
48 | 0 | max = nbr; |
49 | 0 | else if (max->priority == nbr->priority) |
50 | 0 | if (IPV4_ADDR_CMP(&max->router_id, |
51 | 0 | &nbr->router_id) |
52 | 0 | < 0) |
53 | 0 | max = nbr; |
54 | 0 | } |
55 | 0 | } |
56 | | |
57 | 0 | return max; |
58 | 0 | } |
59 | | |
60 | | static struct ospf_neighbor *ospf_elect_dr(struct ospf_interface *oi, |
61 | | struct list *el_list) |
62 | 0 | { |
63 | 0 | struct list *dr_list; |
64 | 0 | struct listnode *node; |
65 | 0 | struct ospf_neighbor *nbr, *dr = NULL, *bdr = NULL; |
66 | |
|
67 | 0 | dr_list = list_new(); |
68 | | |
69 | | /* Add neighbors to the list. */ |
70 | 0 | for (ALL_LIST_ELEMENTS_RO(el_list, node, nbr)) { |
71 | | /* neighbor declared to be DR. */ |
72 | 0 | if (NBR_IS_DR(nbr)) |
73 | 0 | listnode_add(dr_list, nbr); |
74 | | |
75 | | /* Preserve neighbor BDR. */ |
76 | 0 | if (IPV4_ADDR_SAME(&BDR(oi), &nbr->address.u.prefix4)) |
77 | 0 | bdr = nbr; |
78 | 0 | } |
79 | | |
80 | | /* Elect Designated Router. */ |
81 | 0 | if (listcount(dr_list) > 0) |
82 | 0 | dr = ospf_dr_election_sub(dr_list); |
83 | 0 | else |
84 | 0 | dr = bdr; |
85 | | |
86 | | /* Set DR to interface. */ |
87 | 0 | if (dr) |
88 | 0 | DR(oi) = dr->address.u.prefix4; |
89 | 0 | else |
90 | 0 | DR(oi).s_addr = 0; |
91 | |
|
92 | 0 | list_delete(&dr_list); |
93 | |
|
94 | 0 | return dr; |
95 | 0 | } |
96 | | |
97 | | static struct ospf_neighbor *ospf_elect_bdr(struct ospf_interface *oi, |
98 | | struct list *el_list) |
99 | 0 | { |
100 | 0 | struct list *bdr_list, *no_dr_list; |
101 | 0 | struct listnode *node; |
102 | 0 | struct ospf_neighbor *nbr, *bdr = NULL; |
103 | |
|
104 | 0 | bdr_list = list_new(); |
105 | 0 | no_dr_list = list_new(); |
106 | | |
107 | | /* Add neighbors to the list. */ |
108 | 0 | for (ALL_LIST_ELEMENTS_RO(el_list, node, nbr)) { |
109 | | /* neighbor declared to be DR. */ |
110 | 0 | if (NBR_IS_DR(nbr)) |
111 | 0 | continue; |
112 | | |
113 | | /* neighbor declared to be BDR. */ |
114 | 0 | if (NBR_IS_BDR(nbr)) |
115 | 0 | listnode_add(bdr_list, nbr); |
116 | |
|
117 | 0 | listnode_add(no_dr_list, nbr); |
118 | 0 | } |
119 | | |
120 | | /* Elect Backup Designated Router. */ |
121 | 0 | if (listcount(bdr_list) > 0) |
122 | 0 | bdr = ospf_dr_election_sub(bdr_list); |
123 | 0 | else |
124 | 0 | bdr = ospf_dr_election_sub(no_dr_list); |
125 | | |
126 | | /* Set BDR to interface. */ |
127 | 0 | if (bdr) |
128 | 0 | BDR(oi) = bdr->address.u.prefix4; |
129 | 0 | else |
130 | 0 | BDR(oi).s_addr = 0; |
131 | |
|
132 | 0 | list_delete(&bdr_list); |
133 | 0 | list_delete(&no_dr_list); |
134 | |
|
135 | 0 | return bdr; |
136 | 0 | } |
137 | | |
138 | | static int ospf_ism_state(struct ospf_interface *oi) |
139 | 0 | { |
140 | 0 | if (IPV4_ADDR_SAME(&DR(oi), &oi->address->u.prefix4)) |
141 | 0 | return ISM_DR; |
142 | 0 | else if (IPV4_ADDR_SAME(&BDR(oi), &oi->address->u.prefix4)) |
143 | 0 | return ISM_Backup; |
144 | 0 | else |
145 | 0 | return ISM_DROther; |
146 | 0 | } |
147 | | |
148 | | static void ospf_dr_eligible_routers(struct route_table *nbrs, |
149 | | struct list *el_list) |
150 | 0 | { |
151 | 0 | struct route_node *rn; |
152 | 0 | struct ospf_neighbor *nbr; |
153 | |
|
154 | 0 | for (rn = route_top(nbrs); rn; rn = route_next(rn)) |
155 | 0 | if ((nbr = rn->info) != NULL) |
156 | | /* Ignore 0.0.0.0 node*/ |
157 | 0 | if (nbr->router_id.s_addr != INADDR_ANY) |
158 | | /* Is neighbor eligible? */ |
159 | 0 | if (nbr->priority > 0) |
160 | | /* Is neighbor upper 2-Way? */ |
161 | 0 | if (nbr->state >= NSM_TwoWay) |
162 | 0 | listnode_add(el_list, nbr); |
163 | 0 | } |
164 | | |
165 | | /* Generate AdjOK? NSM event. */ |
166 | | static void ospf_dr_change(struct ospf *ospf, struct route_table *nbrs) |
167 | 0 | { |
168 | 0 | struct route_node *rn; |
169 | 0 | struct ospf_neighbor *nbr; |
170 | |
|
171 | 0 | for (rn = route_top(nbrs); rn; rn = route_next(rn)) { |
172 | 0 | nbr = rn->info; |
173 | |
|
174 | 0 | if (!nbr) |
175 | 0 | continue; |
176 | | |
177 | | /* |
178 | | * Ignore 0.0.0.0 node |
179 | | * Is neighbor 2-Way? |
180 | | * Ignore myself |
181 | | */ |
182 | 0 | if (nbr->router_id.s_addr != INADDR_ANY |
183 | 0 | && nbr->state >= NSM_TwoWay |
184 | 0 | && !IPV4_ADDR_SAME(&nbr->router_id, &ospf->router_id)) |
185 | 0 | OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_AdjOK); |
186 | 0 | } |
187 | 0 | } |
188 | | |
189 | | int ospf_dr_election(struct ospf_interface *oi) |
190 | 0 | { |
191 | 0 | struct in_addr old_dr, old_bdr; |
192 | 0 | int old_state, new_state; |
193 | 0 | struct list *el_list; |
194 | | |
195 | | /* backup current values. */ |
196 | 0 | old_dr = DR(oi); |
197 | 0 | old_bdr = BDR(oi); |
198 | 0 | old_state = oi->state; |
199 | |
|
200 | 0 | el_list = list_new(); |
201 | | |
202 | | /* List eligible routers. */ |
203 | 0 | ospf_dr_eligible_routers(oi->nbrs, el_list); |
204 | | |
205 | | /* First election of DR and BDR. */ |
206 | 0 | ospf_elect_bdr(oi, el_list); |
207 | 0 | ospf_elect_dr(oi, el_list); |
208 | |
|
209 | 0 | new_state = ospf_ism_state(oi); |
210 | |
|
211 | 0 | if (IS_DEBUG_OSPF(ism, ISM_STATUS)) { |
212 | 0 | zlog_debug("DR-Election[1st]: Backup %pI4", &BDR(oi)); |
213 | 0 | zlog_debug("DR-Election[1st]: DR %pI4", &DR(oi)); |
214 | 0 | } |
215 | |
|
216 | 0 | if (new_state != old_state |
217 | 0 | && !(new_state == ISM_DROther && old_state < ISM_DROther)) { |
218 | 0 | ospf_elect_bdr(oi, el_list); |
219 | 0 | ospf_elect_dr(oi, el_list); |
220 | |
|
221 | 0 | new_state = ospf_ism_state(oi); |
222 | |
|
223 | 0 | if (IS_DEBUG_OSPF(ism, ISM_STATUS)) { |
224 | 0 | zlog_debug("DR-Election[2nd]: Backup %pI4", &BDR(oi)); |
225 | 0 | zlog_debug("DR-Election[2nd]: DR %pI4", &DR(oi)); |
226 | 0 | } |
227 | 0 | } |
228 | |
|
229 | 0 | list_delete(&el_list); |
230 | | |
231 | | /* if DR or BDR changes, cause AdjOK? neighbor event. */ |
232 | 0 | if (!IPV4_ADDR_SAME(&old_dr, &DR(oi)) |
233 | 0 | || !IPV4_ADDR_SAME(&old_bdr, &BDR(oi))) |
234 | 0 | ospf_dr_change(oi->ospf, oi->nbrs); |
235 | |
|
236 | 0 | return new_state; |
237 | 0 | } |
238 | | |
239 | | |
240 | | void ospf_hello_timer(struct event *thread) |
241 | 0 | { |
242 | 0 | struct ospf_interface *oi; |
243 | |
|
244 | 0 | oi = EVENT_ARG(thread); |
245 | 0 | oi->t_hello = NULL; |
246 | | |
247 | | /* Check if the GR hello-delay is active. */ |
248 | 0 | if (oi->gr.hello_delay.t_grace_send) |
249 | 0 | return; |
250 | | |
251 | 0 | if (IS_DEBUG_OSPF(ism, ISM_TIMERS)) |
252 | 0 | zlog_debug("ISM[%s]: Timer (Hello timer expire)", IF_NAME(oi)); |
253 | | |
254 | | /* Sending hello packet. */ |
255 | 0 | ospf_hello_send(oi); |
256 | | |
257 | | /* Hello timer set. */ |
258 | 0 | OSPF_HELLO_TIMER_ON(oi); |
259 | 0 | } |
260 | | |
261 | | static void ospf_wait_timer(struct event *thread) |
262 | 0 | { |
263 | 0 | struct ospf_interface *oi; |
264 | 0 |
|
265 | 0 | oi = EVENT_ARG(thread); |
266 | 0 | oi->t_wait = NULL; |
267 | 0 |
|
268 | 0 | if (IS_DEBUG_OSPF(ism, ISM_TIMERS)) |
269 | 0 | zlog_debug("ISM[%s]: Timer (Wait timer expire)", IF_NAME(oi)); |
270 | 0 |
|
271 | 0 | OSPF_ISM_EVENT_SCHEDULE(oi, ISM_WaitTimer); |
272 | 0 | } |
273 | | |
274 | | /* Hook function called after ospf ISM event is occurred. And vty's |
275 | | network command invoke this function after making interface |
276 | | structure. */ |
277 | | static void ism_timer_set(struct ospf_interface *oi) |
278 | 0 | { |
279 | 0 | switch (oi->state) { |
280 | 0 | case ISM_Down: |
281 | | /* First entry point of ospf interface state machine. In this |
282 | | state |
283 | | interface parameters must be set to initial values, and |
284 | | timers are |
285 | | reset also. */ |
286 | 0 | EVENT_OFF(oi->t_hello); |
287 | 0 | EVENT_OFF(oi->t_wait); |
288 | 0 | EVENT_OFF(oi->t_ls_ack); |
289 | 0 | EVENT_OFF(oi->gr.hello_delay.t_grace_send); |
290 | 0 | break; |
291 | 0 | case ISM_Loopback: |
292 | | /* In this state, the interface may be looped back and will be |
293 | | unavailable for regular data traffic. */ |
294 | 0 | EVENT_OFF(oi->t_hello); |
295 | 0 | EVENT_OFF(oi->t_wait); |
296 | 0 | EVENT_OFF(oi->t_ls_ack); |
297 | 0 | EVENT_OFF(oi->gr.hello_delay.t_grace_send); |
298 | 0 | break; |
299 | 0 | case ISM_Waiting: |
300 | | /* The router is trying to determine the identity of DRouter and |
301 | | BDRouter. The router begin to receive and send Hello Packets. |
302 | | */ |
303 | | /* send first hello immediately */ |
304 | 0 | OSPF_ISM_TIMER_MSEC_ON(oi->t_hello, ospf_hello_timer, 1); |
305 | 0 | OSPF_ISM_TIMER_ON(oi->t_wait, ospf_wait_timer, |
306 | 0 | OSPF_IF_PARAM(oi, v_wait)); |
307 | 0 | EVENT_OFF(oi->t_ls_ack); |
308 | 0 | break; |
309 | 0 | case ISM_PointToPoint: |
310 | | /* The interface connects to a physical Point-to-point network |
311 | | or |
312 | | virtual link. The router attempts to form an adjacency with |
313 | | neighboring router. Hello packets are also sent. */ |
314 | | /* send first hello immediately */ |
315 | 0 | OSPF_ISM_TIMER_MSEC_ON(oi->t_hello, ospf_hello_timer, 1); |
316 | 0 | EVENT_OFF(oi->t_wait); |
317 | 0 | OSPF_ISM_TIMER_ON(oi->t_ls_ack, ospf_ls_ack_timer, |
318 | 0 | oi->v_ls_ack); |
319 | 0 | break; |
320 | 0 | case ISM_DROther: |
321 | | /* The network type of the interface is broadcast or NBMA |
322 | | network, |
323 | | and the router itself is neither Designated Router nor |
324 | | Backup Designated Router. */ |
325 | 0 | OSPF_HELLO_TIMER_ON(oi); |
326 | 0 | EVENT_OFF(oi->t_wait); |
327 | 0 | OSPF_ISM_TIMER_ON(oi->t_ls_ack, ospf_ls_ack_timer, |
328 | 0 | oi->v_ls_ack); |
329 | 0 | break; |
330 | 0 | case ISM_Backup: |
331 | | /* The network type of the interface is broadcast os NBMA |
332 | | network, |
333 | | and the router is Backup Designated Router. */ |
334 | 0 | OSPF_HELLO_TIMER_ON(oi); |
335 | 0 | EVENT_OFF(oi->t_wait); |
336 | 0 | OSPF_ISM_TIMER_ON(oi->t_ls_ack, ospf_ls_ack_timer, |
337 | 0 | oi->v_ls_ack); |
338 | 0 | break; |
339 | 0 | case ISM_DR: |
340 | | /* The network type of the interface is broadcast or NBMA |
341 | | network, |
342 | | and the router is Designated Router. */ |
343 | 0 | OSPF_HELLO_TIMER_ON(oi); |
344 | 0 | EVENT_OFF(oi->t_wait); |
345 | 0 | OSPF_ISM_TIMER_ON(oi->t_ls_ack, ospf_ls_ack_timer, |
346 | 0 | oi->v_ls_ack); |
347 | 0 | break; |
348 | 0 | } |
349 | 0 | } |
350 | | |
351 | | static int ism_interface_up(struct ospf_interface *oi) |
352 | 0 | { |
353 | 0 | int next_state = 0; |
354 | | |
355 | | /* if network type is point-to-point, Point-to-MultiPoint or virtual |
356 | | link, |
357 | | the state transitions to Point-to-Point. */ |
358 | 0 | if (oi->type == OSPF_IFTYPE_POINTOPOINT |
359 | 0 | || oi->type == OSPF_IFTYPE_POINTOMULTIPOINT |
360 | 0 | || oi->type == OSPF_IFTYPE_VIRTUALLINK) |
361 | 0 | next_state = ISM_PointToPoint; |
362 | | /* Else if the router is not eligible to DR, the state transitions to |
363 | | DROther. */ |
364 | 0 | else if (PRIORITY(oi) == 0) /* router is eligible? */ |
365 | 0 | next_state = ISM_DROther; |
366 | 0 | else |
367 | | /* Otherwise, the state transitions to Waiting. */ |
368 | 0 | next_state = ISM_Waiting; |
369 | |
|
370 | 0 | if (oi->type == OSPF_IFTYPE_NBMA) |
371 | 0 | ospf_nbr_nbma_if_update(oi->ospf, oi); |
372 | | |
373 | | /* ospf_ism_event (t); */ |
374 | 0 | return next_state; |
375 | 0 | } |
376 | | |
377 | | static int ism_loop_ind(struct ospf_interface *oi) |
378 | 0 | { |
379 | | /* call ism_interface_down. */ |
380 | | /* ret = ism_interface_down (oi); */ |
381 | |
|
382 | 0 | return 0; |
383 | 0 | } |
384 | | |
385 | | /* Interface down event handler. */ |
386 | | static int ism_interface_down(struct ospf_interface *oi) |
387 | 0 | { |
388 | 0 | ospf_if_cleanup(oi); |
389 | 0 | return 0; |
390 | 0 | } |
391 | | |
392 | | |
393 | | static int ism_backup_seen(struct ospf_interface *oi) |
394 | 0 | { |
395 | 0 | return ospf_dr_election(oi); |
396 | 0 | } |
397 | | |
398 | | static int ism_wait_timer(struct ospf_interface *oi) |
399 | 0 | { |
400 | 0 | return ospf_dr_election(oi); |
401 | 0 | } |
402 | | |
403 | | static int ism_neighbor_change(struct ospf_interface *oi) |
404 | 0 | { |
405 | 0 | return ospf_dr_election(oi); |
406 | 0 | } |
407 | | |
408 | | static int ism_ignore(struct ospf_interface *oi) |
409 | 0 | { |
410 | 0 | if (IS_DEBUG_OSPF(ism, ISM_EVENTS)) |
411 | 0 | zlog_debug("ISM[%s]: ism_ignore called", IF_NAME(oi)); |
412 | |
|
413 | 0 | return 0; |
414 | 0 | } |
415 | | |
416 | | /* Interface State Machine */ |
417 | | const struct { |
418 | | int (*func)(struct ospf_interface *); |
419 | | int next_state; |
420 | | } ISM[OSPF_ISM_STATE_MAX][OSPF_ISM_EVENT_MAX] = { |
421 | | { |
422 | | /* DependUpon: dummy state. */ |
423 | | {ism_ignore, ISM_DependUpon}, /* NoEvent */ |
424 | | {ism_ignore, ISM_DependUpon}, /* InterfaceUp */ |
425 | | {ism_ignore, ISM_DependUpon}, /* WaitTimer */ |
426 | | {ism_ignore, ISM_DependUpon}, /* BackupSeen */ |
427 | | {ism_ignore, ISM_DependUpon}, /* NeighborChange */ |
428 | | {ism_ignore, ISM_DependUpon}, /* LoopInd */ |
429 | | {ism_ignore, ISM_DependUpon}, /* UnloopInd */ |
430 | | {ism_ignore, ISM_DependUpon}, /* InterfaceDown */ |
431 | | }, |
432 | | { |
433 | | /* Down:*/ |
434 | | {ism_ignore, ISM_DependUpon}, /* NoEvent */ |
435 | | {ism_interface_up, ISM_DependUpon}, /* InterfaceUp */ |
436 | | {ism_ignore, ISM_Down}, /* WaitTimer */ |
437 | | {ism_ignore, ISM_Down}, /* BackupSeen */ |
438 | | {ism_ignore, ISM_Down}, /* NeighborChange */ |
439 | | {ism_loop_ind, ISM_Loopback}, /* LoopInd */ |
440 | | {ism_ignore, ISM_Down}, /* UnloopInd */ |
441 | | {ism_interface_down, ISM_Down}, /* InterfaceDown */ |
442 | | }, |
443 | | { |
444 | | /* Loopback: */ |
445 | | {ism_ignore, ISM_DependUpon}, /* NoEvent */ |
446 | | {ism_ignore, ISM_Loopback}, /* InterfaceUp */ |
447 | | {ism_ignore, ISM_Loopback}, /* WaitTimer */ |
448 | | {ism_ignore, ISM_Loopback}, /* BackupSeen */ |
449 | | {ism_ignore, ISM_Loopback}, /* NeighborChange */ |
450 | | {ism_ignore, ISM_Loopback}, /* LoopInd */ |
451 | | {ism_ignore, ISM_Down}, /* UnloopInd */ |
452 | | {ism_interface_down, ISM_Down}, /* InterfaceDown */ |
453 | | }, |
454 | | { |
455 | | /* Waiting: */ |
456 | | {ism_ignore, ISM_DependUpon}, /* NoEvent */ |
457 | | {ism_ignore, ISM_Waiting}, /* InterfaceUp */ |
458 | | {ism_wait_timer, ISM_DependUpon}, /* WaitTimer */ |
459 | | {ism_backup_seen, ISM_DependUpon}, /* BackupSeen */ |
460 | | {ism_ignore, ISM_Waiting}, /* NeighborChange */ |
461 | | {ism_loop_ind, ISM_Loopback}, /* LoopInd */ |
462 | | {ism_ignore, ISM_Waiting}, /* UnloopInd */ |
463 | | {ism_interface_down, ISM_Down}, /* InterfaceDown */ |
464 | | }, |
465 | | { |
466 | | /* Point-to-Point: */ |
467 | | {ism_ignore, ISM_DependUpon}, /* NoEvent */ |
468 | | {ism_ignore, ISM_PointToPoint}, /* InterfaceUp */ |
469 | | {ism_ignore, ISM_PointToPoint}, /* WaitTimer */ |
470 | | {ism_ignore, ISM_PointToPoint}, /* BackupSeen */ |
471 | | {ism_ignore, ISM_PointToPoint}, /* NeighborChange */ |
472 | | {ism_loop_ind, ISM_Loopback}, /* LoopInd */ |
473 | | {ism_ignore, ISM_PointToPoint}, /* UnloopInd */ |
474 | | {ism_interface_down, ISM_Down}, /* InterfaceDown */ |
475 | | }, |
476 | | { |
477 | | /* DROther: */ |
478 | | {ism_ignore, ISM_DependUpon}, /* NoEvent */ |
479 | | {ism_ignore, ISM_DROther}, /* InterfaceUp */ |
480 | | {ism_ignore, ISM_DROther}, /* WaitTimer */ |
481 | | {ism_ignore, ISM_DROther}, /* BackupSeen */ |
482 | | {ism_neighbor_change, ISM_DependUpon}, /* NeighborChange */ |
483 | | {ism_loop_ind, ISM_Loopback}, /* LoopInd */ |
484 | | {ism_ignore, ISM_DROther}, /* UnloopInd */ |
485 | | {ism_interface_down, ISM_Down}, /* InterfaceDown */ |
486 | | }, |
487 | | { |
488 | | /* Backup: */ |
489 | | {ism_ignore, ISM_DependUpon}, /* NoEvent */ |
490 | | {ism_ignore, ISM_Backup}, /* InterfaceUp */ |
491 | | {ism_ignore, ISM_Backup}, /* WaitTimer */ |
492 | | {ism_ignore, ISM_Backup}, /* BackupSeen */ |
493 | | {ism_neighbor_change, ISM_DependUpon}, /* NeighborChange */ |
494 | | {ism_loop_ind, ISM_Loopback}, /* LoopInd */ |
495 | | {ism_ignore, ISM_Backup}, /* UnloopInd */ |
496 | | {ism_interface_down, ISM_Down}, /* InterfaceDown */ |
497 | | }, |
498 | | { |
499 | | /* DR: */ |
500 | | {ism_ignore, ISM_DependUpon}, /* NoEvent */ |
501 | | {ism_ignore, ISM_DR}, /* InterfaceUp */ |
502 | | {ism_ignore, ISM_DR}, /* WaitTimer */ |
503 | | {ism_ignore, ISM_DR}, /* BackupSeen */ |
504 | | {ism_neighbor_change, ISM_DependUpon}, /* NeighborChange */ |
505 | | {ism_loop_ind, ISM_Loopback}, /* LoopInd */ |
506 | | {ism_ignore, ISM_DR}, /* UnloopInd */ |
507 | | {ism_interface_down, ISM_Down}, /* InterfaceDown */ |
508 | | }, |
509 | | }; |
510 | | |
511 | | static const char *const ospf_ism_event_str[] = { |
512 | | "NoEvent", "InterfaceUp", "WaitTimer", "BackupSeen", |
513 | | "NeighborChange", "LoopInd", "UnLoopInd", "InterfaceDown", |
514 | | }; |
515 | | |
516 | | static void ism_change_state(struct ospf_interface *oi, int state) |
517 | 0 | { |
518 | 0 | int old_state; |
519 | 0 | struct ospf_lsa *lsa; |
520 | | |
521 | | /* Logging change of state. */ |
522 | 0 | if (IS_DEBUG_OSPF(ism, ISM_STATUS)) |
523 | 0 | zlog_debug("ISM[%s]: State change %s -> %s", IF_NAME(oi), |
524 | 0 | lookup_msg(ospf_ism_state_msg, oi->state, NULL), |
525 | 0 | lookup_msg(ospf_ism_state_msg, state, NULL)); |
526 | |
|
527 | 0 | old_state = oi->state; |
528 | 0 | oi->state = state; |
529 | 0 | oi->state_change++; |
530 | |
|
531 | 0 | hook_call(ospf_ism_change, oi, state, old_state); |
532 | | |
533 | | /* Set multicast memberships appropriately for new state. */ |
534 | 0 | ospf_if_set_multicast(oi); |
535 | |
|
536 | 0 | if (old_state == ISM_Down || state == ISM_Down) |
537 | 0 | ospf_check_abr_status(oi->ospf); |
538 | | |
539 | | /* Originate router-LSA. */ |
540 | 0 | if (state == ISM_Down) { |
541 | 0 | if (oi->area->act_ints > 0) |
542 | 0 | oi->area->act_ints--; |
543 | 0 | } else if (old_state == ISM_Down) |
544 | 0 | oi->area->act_ints++; |
545 | | |
546 | | /* schedule router-LSA originate. */ |
547 | 0 | ospf_router_lsa_update_area(oi->area); |
548 | | |
549 | | /* Originate network-LSA. */ |
550 | 0 | if (old_state != ISM_DR && state == ISM_DR) |
551 | 0 | ospf_network_lsa_update(oi); |
552 | 0 | else if (old_state == ISM_DR && state != ISM_DR) { |
553 | | /* Free self originated network LSA. */ |
554 | 0 | lsa = oi->network_lsa_self; |
555 | 0 | if (lsa) |
556 | 0 | ospf_lsa_flush_area(lsa, oi->area); |
557 | |
|
558 | 0 | ospf_lsa_unlock(&oi->network_lsa_self); |
559 | 0 | oi->network_lsa_self = NULL; |
560 | 0 | } |
561 | |
|
562 | 0 | ospf_opaque_ism_change(oi, old_state); |
563 | | |
564 | | /* Check area border status. */ |
565 | 0 | ospf_check_abr_status(oi->ospf); |
566 | 0 | } |
567 | | |
568 | | /* Execute ISM event process. */ |
569 | | void ospf_ism_event(struct event *thread) |
570 | 0 | { |
571 | 0 | int event; |
572 | 0 | int next_state; |
573 | 0 | struct ospf_interface *oi; |
574 | |
|
575 | 0 | oi = EVENT_ARG(thread); |
576 | 0 | event = EVENT_VAL(thread); |
577 | | |
578 | | /* Call function. */ |
579 | 0 | next_state = (*(ISM[oi->state][event].func))(oi); |
580 | |
|
581 | 0 | if (!next_state) |
582 | 0 | next_state = ISM[oi->state][event].next_state; |
583 | |
|
584 | 0 | if (IS_DEBUG_OSPF(ism, ISM_EVENTS)) |
585 | 0 | zlog_debug("ISM[%s]: %s (%s)", IF_NAME(oi), |
586 | 0 | lookup_msg(ospf_ism_state_msg, oi->state, NULL), |
587 | 0 | ospf_ism_event_str[event]); |
588 | | |
589 | | /* If state is changed. */ |
590 | 0 | if (next_state != oi->state) |
591 | 0 | ism_change_state(oi, next_state); |
592 | | |
593 | | /* Make sure timer is set. */ |
594 | 0 | ism_timer_set(oi); |
595 | 0 | } |