]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
10514170 RW |
2 | /* |
3 | * This is an implementation of RFC 3623 Graceful OSPF Restart. | |
4 | * | |
5 | * Copyright 2021 NetDEF (c), All rights reserved. | |
6 | * Copyright 2020 6WIND (c), All rights reserved. | |
10514170 RW |
7 | */ |
8 | ||
9 | #include <zebra.h> | |
10 | ||
11 | #include "memory.h" | |
12 | #include "command.h" | |
13 | #include "table.h" | |
14 | #include "vty.h" | |
15 | #include "log.h" | |
16 | #include "printfrr.h" | |
17 | ||
18 | #include "ospfd/ospfd.h" | |
19 | #include "ospfd/ospf_abr.h" | |
20 | #include "ospfd/ospf_flood.h" | |
21 | #include "ospfd/ospf_ism.h" | |
22 | #include "ospfd/ospf_interface.h" | |
23 | #include "ospfd/ospf_asbr.h" | |
24 | #include "ospfd/ospf_lsa.h" | |
25 | #include "ospfd/ospf_route.h" | |
26 | #include "ospfd/ospf_zebra.h" | |
27 | #include "ospfd/ospf_lsdb.h" | |
28 | #include "ospfd/ospf_neighbor.h" | |
29 | #include "ospfd/ospf_opaque.h" | |
30 | #include "ospfd/ospf_nsm.h" | |
31 | #include "ospfd/ospf_gr.h" | |
32 | #include "ospfd/ospf_errors.h" | |
33 | #include "ospfd/ospf_dump.h" | |
10514170 | 34 | #include "ospfd/ospf_gr_clippy.c" |
10514170 | 35 | |
ab749e7e | 36 | static void ospf_gr_grace_period_expired(struct event *thread); |
10514170 RW |
37 | |
38 | /* Lookup self-originated Grace-LSA in the LSDB. */ | |
39 | static struct ospf_lsa *ospf_gr_lsa_lookup(struct ospf *ospf, | |
40 | struct ospf_area *area) | |
41 | { | |
42 | struct ospf_lsa *lsa; | |
43 | struct in_addr lsa_id; | |
44 | uint32_t lsa_id_host_byte_order; | |
45 | ||
46 | lsa_id_host_byte_order = SET_OPAQUE_LSID(OPAQUE_TYPE_GRACE_LSA, 0); | |
47 | lsa_id.s_addr = htonl(lsa_id_host_byte_order); | |
48 | lsa = ospf_lsa_lookup(ospf, area, OSPF_OPAQUE_LINK_LSA, lsa_id, | |
49 | ospf->router_id); | |
50 | ||
51 | return lsa; | |
52 | } | |
53 | ||
54 | /* Fill in fields of the Grace-LSA that is being originated. */ | |
55 | static void ospf_gr_lsa_body_set(struct ospf_gr_info *gr_info, | |
ab749e7e RW |
56 | struct ospf_interface *oi, |
57 | enum ospf_gr_restart_reason reason, | |
58 | struct stream *s) | |
10514170 RW |
59 | { |
60 | struct grace_tlv_graceperiod tlv_period = {}; | |
61 | struct grace_tlv_restart_reason tlv_reason = {}; | |
62 | struct grace_tlv_restart_addr tlv_address = {}; | |
63 | ||
64 | /* Put grace period. */ | |
65 | tlv_period.header.type = htons(GRACE_PERIOD_TYPE); | |
66 | tlv_period.header.length = htons(GRACE_PERIOD_LENGTH); | |
67 | tlv_period.interval = htonl(gr_info->grace_period); | |
68 | stream_put(s, &tlv_period, sizeof(tlv_period)); | |
69 | ||
70 | /* Put restart reason. */ | |
71 | tlv_reason.header.type = htons(RESTART_REASON_TYPE); | |
72 | tlv_reason.header.length = htons(RESTART_REASON_LENGTH); | |
ab749e7e | 73 | tlv_reason.reason = reason; |
10514170 RW |
74 | stream_put(s, &tlv_reason, sizeof(tlv_reason)); |
75 | ||
76 | /* Put IP address. */ | |
77 | if (oi->type == OSPF_IFTYPE_BROADCAST || oi->type == OSPF_IFTYPE_NBMA | |
78 | || oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) { | |
79 | tlv_address.header.type = htons(RESTARTER_IP_ADDR_TYPE); | |
80 | tlv_address.header.length = htons(RESTARTER_IP_ADDR_LEN); | |
81 | tlv_address.addr = oi->address->u.prefix4; | |
82 | stream_put(s, &tlv_address, sizeof(tlv_address)); | |
83 | } | |
84 | } | |
85 | ||
86 | /* Generate Grace-LSA for a given interface. */ | |
ab749e7e RW |
87 | static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi, |
88 | enum ospf_gr_restart_reason reason) | |
10514170 RW |
89 | { |
90 | struct stream *s; | |
91 | struct lsa_header *lsah; | |
92 | struct ospf_lsa *new; | |
93 | uint8_t options, lsa_type; | |
94 | struct in_addr lsa_id; | |
95 | uint32_t lsa_id_host_byte_order; | |
96 | uint16_t length; | |
97 | ||
98 | /* Create a stream for LSA. */ | |
99 | s = stream_new(OSPF_MAX_LSA_SIZE); | |
100 | ||
101 | lsah = (struct lsa_header *)STREAM_DATA(s); | |
102 | ||
103 | options = LSA_OPTIONS_GET(oi->area); | |
104 | options |= LSA_OPTIONS_NSSA_GET(oi->area); | |
105 | options |= OSPF_OPTION_O; | |
106 | ||
107 | lsa_type = OSPF_OPAQUE_LINK_LSA; | |
108 | lsa_id_host_byte_order = SET_OPAQUE_LSID(OPAQUE_TYPE_GRACE_LSA, 0); | |
109 | lsa_id.s_addr = htonl(lsa_id_host_byte_order); | |
110 | ||
111 | /* Set opaque-LSA header fields. */ | |
112 | lsa_header_set(s, options, lsa_type, lsa_id, oi->ospf->router_id); | |
113 | ||
114 | /* Set opaque-LSA body fields. */ | |
ab749e7e | 115 | ospf_gr_lsa_body_set(&oi->ospf->gr_info, oi, reason, s); |
10514170 RW |
116 | |
117 | /* Set length. */ | |
118 | length = stream_get_endp(s); | |
119 | lsah->length = htons(length); | |
120 | ||
121 | /* Now, create an OSPF LSA instance. */ | |
122 | new = ospf_lsa_new_and_data(length); | |
123 | ||
124 | if (IS_DEBUG_OSPF_GR) | |
125 | zlog_debug("LSA[Type%d:%pI4]: Create an Opaque-LSA/GR instance", | |
126 | lsa_type, &lsa_id); | |
127 | ||
128 | new->area = oi->area; | |
129 | new->oi = oi; | |
130 | SET_FLAG(new->flags, OSPF_LSA_SELF); | |
131 | memcpy(new->data, lsah, length); | |
132 | stream_free(s); | |
133 | ||
134 | return new; | |
135 | } | |
136 | ||
137 | /* Originate and install Grace-LSA for a given interface. */ | |
ab749e7e RW |
138 | static void ospf_gr_lsa_originate(struct ospf_interface *oi, |
139 | enum ospf_gr_restart_reason reason, | |
140 | bool maxage) | |
10514170 RW |
141 | { |
142 | struct ospf_lsa *lsa, *old; | |
143 | ||
ab749e7e RW |
144 | /* Skip originating a Grace-LSA when not necessary. */ |
145 | if (!if_is_operative(oi->ifp) || if_is_loopback(oi->ifp) || | |
146 | (reason != OSPF_GR_UNKNOWN_RESTART && | |
147 | ospf_interface_neighbor_count(oi) == 0)) | |
10514170 RW |
148 | return; |
149 | ||
150 | /* Create new Grace-LSA. */ | |
ab749e7e | 151 | lsa = ospf_gr_lsa_new(oi, reason); |
10514170 RW |
152 | if (!lsa) { |
153 | zlog_warn("%s: ospf_gr_lsa_new() failed", __func__); | |
154 | return; | |
155 | } | |
156 | ||
4a0167fe RW |
157 | if (maxage) |
158 | lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); | |
159 | ||
10514170 RW |
160 | /* Find the old LSA and increase the seqno. */ |
161 | old = ospf_gr_lsa_lookup(oi->ospf, oi->area); | |
162 | if (old) | |
163 | lsa->data->ls_seqnum = lsa_seqnum_increment(old); | |
164 | ||
ab749e7e RW |
165 | if (!maxage && reason == OSPF_GR_UNKNOWN_RESTART) { |
166 | struct list *update; | |
167 | struct in_addr addr; | |
168 | ||
169 | /* | |
170 | * When performing an unplanned restart, send a handcrafted | |
171 | * Grace-LSA since the interface isn't fully initialized yet. | |
172 | */ | |
173 | ospf_lsa_checksum(lsa->data); | |
174 | ospf_lsa_lock(lsa); | |
175 | update = list_new(); | |
176 | listnode_add(update, lsa); | |
177 | addr.s_addr = htonl(OSPF_ALLSPFROUTERS); | |
178 | ospf_ls_upd_queue_send(oi, update, addr, true); | |
179 | list_delete(&update); | |
180 | ospf_lsa_discard(lsa); | |
181 | } else { | |
182 | /* Install this LSA into LSDB. */ | |
183 | if (ospf_lsa_install(oi->ospf, oi, lsa) == NULL) { | |
184 | zlog_warn("%s: ospf_lsa_install() failed", __func__); | |
185 | ospf_lsa_unlock(&lsa); | |
186 | return; | |
187 | } | |
188 | ||
189 | /* Flood the LSA through out the interface */ | |
190 | ospf_flood_through_interface(oi, NULL, lsa); | |
10514170 RW |
191 | } |
192 | ||
193 | /* Update new LSA origination count. */ | |
194 | oi->ospf->lsa_originate_count++; | |
10514170 RW |
195 | } |
196 | ||
10514170 RW |
197 | /* Flush all self-originated Grace-LSAs. */ |
198 | static void ospf_gr_flush_grace_lsas(struct ospf *ospf) | |
199 | { | |
200 | struct ospf_area *area; | |
201 | struct listnode *anode; | |
202 | ||
203 | for (ALL_LIST_ELEMENTS_RO(ospf->areas, anode, area)) { | |
10514170 RW |
204 | struct ospf_interface *oi; |
205 | struct listnode *inode; | |
206 | ||
ab749e7e RW |
207 | for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi)) { |
208 | if (IS_DEBUG_OSPF_GR) | |
209 | zlog_debug( | |
210 | "GR: flushing self-originated Grace-LSA [area %pI4] [interface %s]", | |
211 | &area->area_id, oi->ifp->name); | |
10514170 | 212 | |
ab749e7e RW |
213 | ospf_gr_lsa_originate(oi, ospf->gr_info.reason, true); |
214 | } | |
10514170 RW |
215 | } |
216 | } | |
217 | ||
218 | /* Exit from the Graceful Restart mode. */ | |
219 | static void ospf_gr_restart_exit(struct ospf *ospf, const char *reason) | |
220 | { | |
221 | struct ospf_area *area; | |
222 | struct listnode *onode, *anode; | |
223 | ||
224 | if (IS_DEBUG_OSPF_GR) | |
225 | zlog_debug("GR: exiting graceful restart: %s", reason); | |
226 | ||
227 | ospf->gr_info.restart_in_progress = false; | |
e16d030c | 228 | EVENT_OFF(ospf->gr_info.t_grace_period); |
10514170 | 229 | |
10514170 RW |
230 | for (ALL_LIST_ELEMENTS_RO(ospf->areas, onode, area)) { |
231 | struct ospf_interface *oi; | |
232 | ||
233 | /* | |
234 | * 1) The router should reoriginate its router-LSAs for all | |
235 | * attached areas in order to make sure they have the correct | |
236 | * contents. | |
237 | */ | |
238 | ospf_router_lsa_update_area(area); | |
239 | ||
0c05ceae RW |
240 | for (ALL_LIST_ELEMENTS_RO(area->oiflist, anode, oi)) { |
241 | /* Disable hello delay. */ | |
242 | if (oi->gr.hello_delay.t_grace_send) { | |
243 | oi->gr.hello_delay.elapsed_seconds = 0; | |
244 | EVENT_OFF(oi->gr.hello_delay.t_grace_send); | |
245 | OSPF_ISM_TIMER_MSEC_ON(oi->t_hello, | |
246 | ospf_hello_timer, 1); | |
247 | } | |
248 | ||
249 | /* | |
250 | * 2) The router should reoriginate network-LSAs on all | |
251 | * segments where it is the Designated Router. | |
252 | */ | |
10514170 RW |
253 | if (oi->state == ISM_DR) |
254 | ospf_network_lsa_update(oi); | |
0c05ceae | 255 | } |
10514170 RW |
256 | } |
257 | ||
258 | /* | |
259 | * 5) Any received self-originated LSAs that are no longer valid should | |
260 | * be flushed. | |
261 | */ | |
262 | ospf_schedule_abr_task(ospf); | |
263 | ||
264 | /* | |
265 | * 3) The router reruns its OSPF routing calculations, this time | |
266 | * installing the results into the system forwarding table, and | |
267 | * originating summary-LSAs, Type-7 LSAs and AS-external-LSAs as | |
268 | * necessary. | |
269 | * | |
270 | * 4) Any remnant entries in the system forwarding table that were | |
271 | * installed before the restart, but that are no longer valid, | |
272 | * should be removed. | |
273 | */ | |
274 | ospf->gr_info.finishing_restart = true; | |
ab749e7e RW |
275 | XFREE(MTYPE_TMP, ospf->gr_info.exit_reason); |
276 | ospf->gr_info.exit_reason = XSTRDUP(MTYPE_TMP, reason); | |
10514170 RW |
277 | ospf_spf_calculate_schedule(ospf, SPF_FLAG_GR_FINISH); |
278 | ||
279 | /* 6) Any grace-LSAs that the router originated should be flushed. */ | |
280 | ospf_gr_flush_grace_lsas(ospf); | |
281 | } | |
282 | ||
ab749e7e RW |
283 | /* Enter the Graceful Restart mode. */ |
284 | void ospf_gr_restart_enter(struct ospf *ospf, | |
2882096f | 285 | enum ospf_gr_restart_reason reason, time_t timestamp) |
ab749e7e RW |
286 | { |
287 | unsigned long remaining_time; | |
288 | ||
289 | ospf->gr_info.restart_in_progress = true; | |
290 | ospf->gr_info.reason = reason; | |
291 | ||
292 | /* Schedule grace period timeout. */ | |
293 | remaining_time = timestamp - time(NULL); | |
294 | if (IS_DEBUG_OSPF_GR) | |
295 | zlog_debug( | |
296 | "GR: remaining time until grace period expires: %lu(s)", | |
297 | remaining_time); | |
298 | ||
299 | event_add_timer(master, ospf_gr_grace_period_expired, ospf, | |
300 | remaining_time, &ospf->gr_info.t_grace_period); | |
301 | } | |
302 | ||
10514170 RW |
303 | /* Check if a Router-LSA contains a given link. */ |
304 | static bool ospf_router_lsa_contains_adj(struct ospf_lsa *lsa, | |
305 | struct in_addr *id) | |
306 | { | |
307 | struct router_lsa *rl; | |
308 | ||
309 | rl = (struct router_lsa *)lsa->data; | |
310 | for (int i = 0; i < ntohs(rl->links); i++) { | |
311 | struct in_addr *link_id = &rl->link[i].link_id; | |
312 | ||
313 | if (rl->link[i].type != LSA_LINK_TYPE_POINTOPOINT) | |
314 | continue; | |
315 | ||
316 | if (IPV4_ADDR_SAME(id, link_id)) | |
317 | return true; | |
318 | } | |
319 | ||
320 | return false; | |
321 | } | |
322 | ||
323 | static bool ospf_gr_check_router_lsa_consistency(struct ospf *ospf, | |
324 | struct ospf_area *area, | |
325 | struct ospf_lsa *lsa) | |
326 | { | |
327 | if (CHECK_FLAG(lsa->flags, OSPF_LSA_SELF)) { | |
328 | struct ospf_lsa *lsa_self = lsa; | |
329 | struct router_lsa *rl = (struct router_lsa *)lsa->data; | |
330 | ||
331 | for (int i = 0; i < ntohs(rl->links); i++) { | |
332 | struct in_addr *link_id = &rl->link[i].link_id; | |
333 | struct ospf_lsa *lsa_adj; | |
334 | ||
335 | if (rl->link[i].type != LSA_LINK_TYPE_POINTOPOINT) | |
336 | continue; | |
337 | ||
338 | lsa_adj = ospf_lsa_lookup_by_id(area, OSPF_ROUTER_LSA, | |
339 | *link_id); | |
340 | if (!lsa_adj) | |
341 | continue; | |
342 | ||
343 | if (!ospf_router_lsa_contains_adj(lsa_adj, | |
344 | &lsa_self->data->id)) | |
345 | return false; | |
346 | } | |
347 | } else { | |
348 | struct ospf_lsa *lsa_self; | |
349 | ||
350 | lsa_self = ospf_lsa_lookup_by_id(area, OSPF_ROUTER_LSA, | |
351 | ospf->router_id); | |
352 | if (!lsa_self | |
353 | || !CHECK_FLAG(lsa_self->flags, OSPF_LSA_RECEIVED)) | |
354 | return true; | |
355 | ||
356 | if (ospf_router_lsa_contains_adj(lsa, &ospf->router_id) | |
357 | != ospf_router_lsa_contains_adj(lsa_self, &lsa->data->id)) | |
358 | return false; | |
359 | } | |
360 | ||
361 | return true; | |
362 | } | |
363 | ||
364 | /* | |
365 | * Check for LSAs that are inconsistent with the pre-restart LSAs, and abort the | |
366 | * ongoing graceful restart when that's the case. | |
367 | */ | |
368 | void ospf_gr_check_lsdb_consistency(struct ospf *ospf, struct ospf_area *area) | |
369 | { | |
370 | struct route_node *rn; | |
371 | struct ospf_lsa *lsa; | |
372 | ||
373 | for (rn = route_top(ROUTER_LSDB(area)); rn; rn = route_next(rn)) { | |
374 | lsa = rn->info; | |
375 | if (!lsa) | |
376 | continue; | |
377 | ||
378 | if (!ospf_gr_check_router_lsa_consistency(ospf, area, lsa)) { | |
379 | char reason[256]; | |
380 | ||
381 | snprintfrr(reason, sizeof(reason), | |
382 | "detected inconsistent LSA[%s] [area %pI4]", | |
383 | dump_lsa_key(lsa), &area->area_id); | |
384 | ospf_gr_restart_exit(ospf, reason); | |
385 | route_unlock_node(rn); | |
386 | return; | |
387 | } | |
388 | } | |
389 | } | |
390 | ||
391 | /* Lookup neighbor by address in a given OSPF area. */ | |
392 | static struct ospf_neighbor * | |
393 | ospf_area_nbr_lookup_by_addr(struct ospf_area *area, struct in_addr *addr) | |
394 | { | |
395 | struct ospf_interface *oi; | |
396 | struct ospf_neighbor *nbr; | |
397 | struct listnode *node; | |
398 | ||
399 | for (ALL_LIST_ELEMENTS_RO(area->oiflist, node, oi)) { | |
400 | nbr = ospf_nbr_lookup_by_addr(oi->nbrs, addr); | |
401 | if (nbr) | |
402 | return nbr; | |
403 | } | |
404 | ||
405 | return NULL; | |
406 | } | |
407 | ||
408 | /* Lookup neighbor by Router ID in a given OSPF area. */ | |
409 | static struct ospf_neighbor * | |
410 | ospf_area_nbr_lookup_by_routerid(struct ospf_area *area, struct in_addr *id) | |
411 | { | |
412 | struct ospf_interface *oi; | |
413 | struct ospf_neighbor *nbr; | |
414 | struct listnode *node; | |
415 | ||
416 | for (ALL_LIST_ELEMENTS_RO(area->oiflist, node, oi)) { | |
417 | nbr = ospf_nbr_lookup_by_routerid(oi->nbrs, id); | |
418 | if (nbr) | |
419 | return nbr; | |
420 | } | |
421 | ||
422 | return NULL; | |
423 | } | |
424 | ||
425 | /* Check if there's a fully formed adjacency with the given neighbor ID. */ | |
426 | static bool ospf_gr_check_adj_id(struct ospf_area *area, | |
427 | struct in_addr *nbr_id) | |
428 | { | |
429 | struct ospf_neighbor *nbr; | |
430 | ||
431 | nbr = ospf_area_nbr_lookup_by_routerid(area, nbr_id); | |
432 | if (!nbr || nbr->state < NSM_Full) { | |
433 | if (IS_DEBUG_OSPF_GR) | |
434 | zlog_debug("GR: missing adjacency to router %pI4", | |
435 | nbr_id); | |
436 | return false; | |
437 | } | |
438 | ||
439 | return true; | |
440 | } | |
441 | ||
442 | static bool ospf_gr_check_adjs_lsa_transit(struct ospf_area *area, | |
443 | struct in_addr *link_id) | |
444 | { | |
445 | struct ospf *ospf = area->ospf; | |
446 | struct ospf_interface *oi; | |
447 | ||
448 | /* | |
449 | * Check if the transit network refers to a local interface (in which | |
450 | * case it must be a DR for that network). | |
451 | */ | |
452 | oi = ospf_if_lookup_by_local_addr(ospf, NULL, *link_id); | |
453 | if (oi) { | |
454 | struct ospf_lsa *lsa; | |
455 | struct network_lsa *nlsa; | |
456 | size_t cnt; | |
457 | ||
458 | /* Lookup Network LSA corresponding to this interface. */ | |
459 | lsa = ospf_lsa_lookup_by_id(area, OSPF_NETWORK_LSA, *link_id); | |
460 | if (!lsa) | |
461 | return false; | |
462 | ||
463 | /* Iterate over all routers present in the network. */ | |
464 | nlsa = (struct network_lsa *)lsa->data; | |
465 | cnt = (lsa->size - (OSPF_LSA_HEADER_SIZE + 4)) / 4; | |
466 | for (size_t i = 0; i < cnt; i++) { | |
467 | struct in_addr *nbr_id = &nlsa->routers[i]; | |
468 | ||
469 | /* Skip self in the pseudonode. */ | |
470 | if (IPV4_ADDR_SAME(nbr_id, &ospf->router_id)) | |
471 | continue; | |
472 | ||
473 | /* | |
474 | * Check if there's a fully formed adjacency with this | |
475 | * router. | |
476 | */ | |
477 | if (!ospf_gr_check_adj_id(area, nbr_id)) | |
478 | return false; | |
479 | } | |
480 | } else { | |
481 | struct ospf_neighbor *nbr; | |
482 | ||
483 | /* Check if there's a fully formed adjacency with the DR. */ | |
484 | nbr = ospf_area_nbr_lookup_by_addr(area, link_id); | |
485 | if (!nbr || nbr->state < NSM_Full) { | |
486 | if (IS_DEBUG_OSPF_GR) | |
487 | zlog_debug( | |
488 | "GR: missing adjacency to DR router %pI4", | |
489 | link_id); | |
490 | return false; | |
491 | } | |
492 | } | |
493 | ||
494 | return true; | |
495 | } | |
496 | ||
497 | static bool ospf_gr_check_adjs_lsa(struct ospf_area *area, struct ospf_lsa *lsa) | |
498 | { | |
499 | struct router_lsa *rl = (struct router_lsa *)lsa->data; | |
500 | ||
501 | for (int i = 0; i < ntohs(rl->links); i++) { | |
502 | struct in_addr *link_id = &rl->link[i].link_id; | |
503 | ||
504 | switch (rl->link[i].type) { | |
505 | case LSA_LINK_TYPE_POINTOPOINT: | |
506 | if (!ospf_gr_check_adj_id(area, link_id)) | |
507 | return false; | |
508 | break; | |
509 | case LSA_LINK_TYPE_TRANSIT: | |
510 | if (!ospf_gr_check_adjs_lsa_transit(area, link_id)) | |
511 | return false; | |
512 | break; | |
513 | default: | |
514 | break; | |
515 | } | |
516 | } | |
517 | ||
518 | return true; | |
519 | } | |
520 | ||
521 | /* | |
522 | * Check if all adjacencies prior to the restart were reestablished. | |
523 | * | |
524 | * This is done using pre-restart Router LSAs and pre-restart Network LSAs | |
525 | * received from the helping neighbors. | |
526 | */ | |
527 | void ospf_gr_check_adjs(struct ospf *ospf) | |
528 | { | |
529 | struct ospf_area *area; | |
530 | struct listnode *node; | |
531 | ||
532 | for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { | |
533 | struct ospf_lsa *lsa_self; | |
534 | ||
535 | lsa_self = ospf_lsa_lookup_by_id(area, OSPF_ROUTER_LSA, | |
536 | ospf->router_id); | |
537 | if (!lsa_self || !ospf_gr_check_adjs_lsa(area, lsa_self)) { | |
538 | if (IS_DEBUG_OSPF_GR) | |
539 | zlog_debug( | |
540 | "GR: not all adjacencies were reestablished yet [area %pI4]", | |
541 | &area->area_id); | |
542 | return; | |
543 | } | |
544 | } | |
545 | ||
546 | ospf_gr_restart_exit(ospf, "all adjacencies were reestablished"); | |
547 | } | |
548 | ||
549 | /* Handling of grace period expiry. */ | |
e6685141 | 550 | static void ospf_gr_grace_period_expired(struct event *thread) |
10514170 | 551 | { |
e16d030c | 552 | struct ospf *ospf = EVENT_ARG(thread); |
10514170 RW |
553 | |
554 | ospf->gr_info.t_grace_period = NULL; | |
555 | ospf_gr_restart_exit(ospf, "grace period has expired"); | |
10514170 RW |
556 | } |
557 | ||
558 | /* | |
559 | * Returns the path of the file (non-volatile memory) that contains GR status | |
560 | * information. | |
561 | */ | |
562 | static char *ospf_gr_nvm_filepath(struct ospf *ospf) | |
563 | { | |
564 | static char filepath[MAXPATHLEN]; | |
565 | char instance[16] = ""; | |
566 | ||
567 | if (ospf->instance) | |
568 | snprintf(instance, sizeof(instance), "-%d", ospf->instance); | |
569 | snprintf(filepath, sizeof(filepath), OSPFD_GR_STATE, instance); | |
570 | return filepath; | |
571 | } | |
572 | ||
0c05ceae RW |
573 | /* Send extra Grace-LSA out the interface (unplanned outages only). */ |
574 | void ospf_gr_iface_send_grace_lsa(struct event *thread) | |
575 | { | |
576 | struct ospf_interface *oi = EVENT_ARG(thread); | |
577 | struct ospf_if_params *params = IF_DEF_PARAMS(oi->ifp); | |
578 | ||
579 | ospf_gr_lsa_originate(oi, oi->ospf->gr_info.reason, false); | |
580 | ||
581 | if (++oi->gr.hello_delay.elapsed_seconds < params->v_gr_hello_delay) | |
582 | event_add_timer(master, ospf_gr_iface_send_grace_lsa, oi, 1, | |
583 | &oi->gr.hello_delay.t_grace_send); | |
584 | else | |
585 | OSPF_ISM_TIMER_MSEC_ON(oi->t_hello, ospf_hello_timer, 1); | |
586 | } | |
587 | ||
10514170 RW |
588 | /* |
589 | * Record in non-volatile memory that the given OSPF instance is attempting to | |
590 | * perform a graceful restart. | |
591 | */ | |
ab749e7e | 592 | static void ospf_gr_nvm_update(struct ospf *ospf, bool prepare) |
10514170 RW |
593 | { |
594 | char *filepath; | |
595 | const char *inst_name; | |
596 | json_object *json; | |
597 | json_object *json_instances; | |
598 | json_object *json_instance; | |
599 | ||
600 | filepath = ospf_gr_nvm_filepath(ospf); | |
44076f4d | 601 | inst_name = ospf_get_name(ospf); |
10514170 RW |
602 | |
603 | json = json_object_from_file(filepath); | |
604 | if (json == NULL) | |
605 | json = json_object_new_object(); | |
606 | ||
607 | json_object_object_get_ex(json, "instances", &json_instances); | |
608 | if (!json_instances) { | |
609 | json_instances = json_object_new_object(); | |
610 | json_object_object_add(json, "instances", json_instances); | |
611 | } | |
612 | ||
613 | json_object_object_get_ex(json_instances, inst_name, &json_instance); | |
614 | if (!json_instance) { | |
615 | json_instance = json_object_new_object(); | |
616 | json_object_object_add(json_instances, inst_name, | |
617 | json_instance); | |
618 | } | |
619 | ||
ab749e7e RW |
620 | json_object_int_add(json_instance, "gracePeriod", |
621 | ospf->gr_info.grace_period); | |
622 | ||
10514170 RW |
623 | /* |
624 | * Record not only the grace period, but also a UNIX timestamp | |
625 | * corresponding to the end of that period. That way, once ospfd is | |
626 | * restarted, it will be possible to take into account the time that | |
627 | * passed while ospfd wasn't running. | |
628 | */ | |
ab749e7e RW |
629 | if (prepare) |
630 | json_object_int_add(json_instance, "timestamp", | |
631 | time(NULL) + ospf->gr_info.grace_period); | |
10514170 RW |
632 | |
633 | json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY); | |
634 | json_object_free(json); | |
635 | } | |
636 | ||
637 | /* | |
638 | * Delete GR status information about the given OSPF instance from non-volatile | |
639 | * memory. | |
640 | */ | |
ab749e7e | 641 | void ospf_gr_nvm_delete(struct ospf *ospf) |
10514170 RW |
642 | { |
643 | char *filepath; | |
644 | const char *inst_name; | |
645 | json_object *json; | |
646 | json_object *json_instances; | |
647 | ||
648 | filepath = ospf_gr_nvm_filepath(ospf); | |
44076f4d | 649 | inst_name = ospf_get_name(ospf); |
10514170 RW |
650 | |
651 | json = json_object_from_file(filepath); | |
652 | if (json == NULL) | |
653 | json = json_object_new_object(); | |
654 | ||
655 | json_object_object_get_ex(json, "instances", &json_instances); | |
656 | if (!json_instances) { | |
657 | json_instances = json_object_new_object(); | |
658 | json_object_object_add(json, "instances", json_instances); | |
659 | } | |
660 | ||
661 | json_object_object_del(json_instances, inst_name); | |
662 | ||
663 | json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY); | |
664 | json_object_free(json); | |
665 | } | |
666 | ||
667 | /* | |
668 | * Fetch from non-volatile memory whether the given OSPF instance is performing | |
669 | * a graceful shutdown or not. | |
670 | */ | |
671 | void ospf_gr_nvm_read(struct ospf *ospf) | |
672 | { | |
673 | char *filepath; | |
674 | const char *inst_name; | |
675 | json_object *json; | |
676 | json_object *json_instances; | |
677 | json_object *json_instance; | |
678 | json_object *json_timestamp; | |
ab749e7e | 679 | json_object *json_grace_period; |
10514170 RW |
680 | time_t timestamp = 0; |
681 | ||
682 | filepath = ospf_gr_nvm_filepath(ospf); | |
44076f4d | 683 | inst_name = ospf_get_name(ospf); |
10514170 RW |
684 | |
685 | json = json_object_from_file(filepath); | |
686 | if (json == NULL) | |
687 | json = json_object_new_object(); | |
688 | ||
689 | json_object_object_get_ex(json, "instances", &json_instances); | |
690 | if (!json_instances) { | |
691 | json_instances = json_object_new_object(); | |
692 | json_object_object_add(json, "instances", json_instances); | |
693 | } | |
694 | ||
695 | json_object_object_get_ex(json_instances, inst_name, &json_instance); | |
696 | if (!json_instance) { | |
697 | json_instance = json_object_new_object(); | |
698 | json_object_object_add(json_instances, inst_name, | |
699 | json_instance); | |
700 | } | |
701 | ||
ab749e7e RW |
702 | json_object_object_get_ex(json_instance, "gracePeriod", |
703 | &json_grace_period); | |
10514170 | 704 | json_object_object_get_ex(json_instance, "timestamp", &json_timestamp); |
ab749e7e | 705 | |
10514170 RW |
706 | if (json_timestamp) { |
707 | time_t now; | |
10514170 | 708 | |
ab749e7e | 709 | /* Planned GR: check if the grace period has already expired. */ |
10514170 RW |
710 | now = time(NULL); |
711 | timestamp = json_object_get_int(json_timestamp); | |
712 | if (now > timestamp) { | |
713 | ospf_gr_restart_exit( | |
714 | ospf, "grace period has expired already"); | |
ab749e7e RW |
715 | } else |
716 | ospf_gr_restart_enter(ospf, OSPF_GR_SW_RESTART, | |
717 | timestamp); | |
718 | } else if (json_grace_period) { | |
719 | uint32_t grace_period; | |
720 | ||
721 | /* | |
722 | * Unplanned GR: the Grace-LSAs will be sent later as soon as | |
723 | * the interfaces are operational. | |
724 | */ | |
725 | grace_period = json_object_get_int(json_grace_period); | |
726 | ospf->gr_info.grace_period = grace_period; | |
727 | ospf_gr_restart_enter(ospf, OSPF_GR_UNKNOWN_RESTART, | |
728 | time(NULL) + ospf->gr_info.grace_period); | |
10514170 RW |
729 | } |
730 | ||
731 | json_object_object_del(json_instances, inst_name); | |
732 | ||
733 | json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY); | |
734 | json_object_free(json); | |
735 | } | |
736 | ||
ab749e7e RW |
737 | void ospf_gr_unplanned_start_interface(struct ospf_interface *oi) |
738 | { | |
739 | /* Send Grace-LSA. */ | |
740 | ospf_gr_lsa_originate(oi, oi->ospf->gr_info.reason, false); | |
0c05ceae RW |
741 | |
742 | /* Start GR hello-delay interval. */ | |
743 | oi->gr.hello_delay.elapsed_seconds = 0; | |
744 | event_add_timer(master, ospf_gr_iface_send_grace_lsa, oi, 1, | |
745 | &oi->gr.hello_delay.t_grace_send); | |
ab749e7e RW |
746 | } |
747 | ||
10514170 RW |
748 | /* Prepare to start a Graceful Restart. */ |
749 | static void ospf_gr_prepare(void) | |
750 | { | |
751 | struct ospf *ospf; | |
752 | struct ospf_interface *oi; | |
753 | struct listnode *onode; | |
754 | ||
755 | for (ALL_LIST_ELEMENTS_RO(om->ospf, onode, ospf)) { | |
756 | struct listnode *inode; | |
757 | ||
758 | if (!ospf->gr_info.restart_support | |
759 | || ospf->gr_info.prepare_in_progress) | |
760 | continue; | |
761 | ||
762 | if (IS_DEBUG_OSPF_GR) | |
763 | zlog_debug( | |
764 | "GR: preparing to perform a graceful restart [period %u second(s)] [vrf %s]", | |
765 | ospf->gr_info.grace_period, | |
766 | ospf_vrf_id_to_name(ospf->vrf_id)); | |
767 | ||
768 | if (!CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE)) { | |
769 | zlog_warn( | |
770 | "%s: failed to activate graceful restart: opaque capability not enabled", | |
771 | __func__); | |
772 | continue; | |
773 | } | |
774 | ||
10514170 RW |
775 | /* Send a Grace-LSA to all neighbors. */ |
776 | for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, inode, oi)) | |
ab749e7e | 777 | ospf_gr_lsa_originate(oi, OSPF_GR_SW_RESTART, false); |
10514170 RW |
778 | |
779 | /* Record end of the grace period in non-volatile memory. */ | |
ab749e7e | 780 | ospf_gr_nvm_update(ospf, true); |
10514170 RW |
781 | |
782 | /* | |
783 | * Mark that a Graceful Restart preparation is in progress, to | |
784 | * prevent ospfd from flushing its self-originated LSAs on exit. | |
785 | */ | |
786 | ospf->gr_info.prepare_in_progress = true; | |
787 | } | |
788 | } | |
789 | ||
790 | DEFPY(graceful_restart_prepare, graceful_restart_prepare_cmd, | |
791 | "graceful-restart prepare ip ospf", | |
792 | "Graceful Restart commands\n" | |
793 | "Prepare upcoming graceful restart\n" | |
794 | IP_STR | |
4a3c92de | 795 | "Prepare to restart the OSPF process\n") |
10514170 | 796 | { |
eedc80c1 RW |
797 | struct ospf *ospf; |
798 | struct listnode *node; | |
799 | ||
800 | for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { | |
801 | if (!CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE)) { | |
802 | vty_out(vty, | |
803 | "%% Can't start graceful restart: opaque capability not enabled (VRF %s)\n\n", | |
804 | ospf_get_name(ospf)); | |
805 | return CMD_WARNING; | |
806 | } | |
807 | } | |
808 | ||
10514170 RW |
809 | ospf_gr_prepare(); |
810 | ||
811 | return CMD_SUCCESS; | |
812 | } | |
813 | ||
814 | DEFPY(graceful_restart, graceful_restart_cmd, | |
815 | "graceful-restart [grace-period (1-1800)$grace_period]", | |
816 | OSPF_GR_STR | |
817 | "Maximum length of the 'grace period'\n" | |
818 | "Maximum length of the 'grace period' in seconds\n") | |
819 | { | |
820 | VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); | |
821 | ||
822 | /* Check and get restart period if present. */ | |
823 | if (!grace_period_str) | |
824 | grace_period = OSPF_DFLT_GRACE_INTERVAL; | |
825 | ||
826 | ospf->gr_info.restart_support = true; | |
827 | ospf->gr_info.grace_period = grace_period; | |
828 | ||
ab749e7e RW |
829 | /* Freeze OSPF routes in the RIB. */ |
830 | (void)ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period); | |
831 | ||
832 | /* Record that GR is enabled in non-volatile memory. */ | |
833 | ospf_gr_nvm_update(ospf, false); | |
834 | ||
10514170 RW |
835 | return CMD_SUCCESS; |
836 | } | |
837 | ||
838 | DEFPY(no_graceful_restart, no_graceful_restart_cmd, | |
9a8c0f2d | 839 | "no graceful-restart [grace-period (1-1800)]", |
10514170 RW |
840 | NO_STR OSPF_GR_STR |
841 | "Maximum length of the 'grace period'\n" | |
842 | "Maximum length of the 'grace period' in seconds\n") | |
843 | { | |
844 | VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); | |
845 | ||
846 | if (!ospf->gr_info.restart_support) | |
847 | return CMD_SUCCESS; | |
848 | ||
849 | if (ospf->gr_info.prepare_in_progress) { | |
850 | vty_out(vty, | |
851 | "%% Error: Graceful Restart preparation in progress\n"); | |
852 | return CMD_WARNING; | |
853 | } | |
854 | ||
855 | ospf->gr_info.restart_support = false; | |
856 | ospf->gr_info.grace_period = OSPF_DFLT_GRACE_INTERVAL; | |
ab749e7e RW |
857 | ospf_gr_nvm_delete(ospf); |
858 | ospf_zebra_gr_disable(ospf); | |
10514170 RW |
859 | |
860 | return CMD_SUCCESS; | |
861 | } | |
862 | ||
863 | void ospf_gr_init(void) | |
864 | { | |
865 | install_element(ENABLE_NODE, &graceful_restart_prepare_cmd); | |
866 | install_element(OSPF_NODE, &graceful_restart_cmd); | |
867 | install_element(OSPF_NODE, &no_graceful_restart_cmd); | |
868 | } |