]>
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 RW |
35 | |
36 | static void ospf_gr_nvm_delete(struct ospf *ospf); | |
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, | |
56 | struct ospf_interface *oi, struct stream *s) | |
57 | { | |
58 | struct grace_tlv_graceperiod tlv_period = {}; | |
59 | struct grace_tlv_restart_reason tlv_reason = {}; | |
60 | struct grace_tlv_restart_addr tlv_address = {}; | |
61 | ||
62 | /* Put grace period. */ | |
63 | tlv_period.header.type = htons(GRACE_PERIOD_TYPE); | |
64 | tlv_period.header.length = htons(GRACE_PERIOD_LENGTH); | |
65 | tlv_period.interval = htonl(gr_info->grace_period); | |
66 | stream_put(s, &tlv_period, sizeof(tlv_period)); | |
67 | ||
68 | /* Put restart reason. */ | |
69 | tlv_reason.header.type = htons(RESTART_REASON_TYPE); | |
70 | tlv_reason.header.length = htons(RESTART_REASON_LENGTH); | |
71 | if (gr_info->restart_support) | |
72 | tlv_reason.reason = OSPF_GR_SW_RESTART; | |
73 | else | |
74 | tlv_reason.reason = OSPF_GR_UNKNOWN_RESTART; | |
75 | stream_put(s, &tlv_reason, sizeof(tlv_reason)); | |
76 | ||
77 | /* Put IP address. */ | |
78 | if (oi->type == OSPF_IFTYPE_BROADCAST || oi->type == OSPF_IFTYPE_NBMA | |
79 | || oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) { | |
80 | tlv_address.header.type = htons(RESTARTER_IP_ADDR_TYPE); | |
81 | tlv_address.header.length = htons(RESTARTER_IP_ADDR_LEN); | |
82 | tlv_address.addr = oi->address->u.prefix4; | |
83 | stream_put(s, &tlv_address, sizeof(tlv_address)); | |
84 | } | |
85 | } | |
86 | ||
87 | /* Generate Grace-LSA for a given interface. */ | |
88 | static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi) | |
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. */ | |
115 | ospf_gr_lsa_body_set(&oi->ospf->gr_info, oi, s); | |
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. */ | |
4a0167fe | 138 | static void ospf_gr_lsa_originate(struct ospf_interface *oi, bool maxage) |
10514170 RW |
139 | { |
140 | struct ospf_lsa *lsa, *old; | |
141 | ||
142 | if (ospf_interface_neighbor_count(oi) == 0) | |
143 | return; | |
144 | ||
145 | /* Create new Grace-LSA. */ | |
146 | lsa = ospf_gr_lsa_new(oi); | |
147 | if (!lsa) { | |
148 | zlog_warn("%s: ospf_gr_lsa_new() failed", __func__); | |
149 | return; | |
150 | } | |
151 | ||
4a0167fe RW |
152 | if (maxage) |
153 | lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); | |
154 | ||
10514170 RW |
155 | /* Find the old LSA and increase the seqno. */ |
156 | old = ospf_gr_lsa_lookup(oi->ospf, oi->area); | |
157 | if (old) | |
158 | lsa->data->ls_seqnum = lsa_seqnum_increment(old); | |
159 | ||
160 | /* Install this LSA into LSDB. */ | |
161 | if (ospf_lsa_install(oi->ospf, oi, lsa) == NULL) { | |
162 | zlog_warn("%s: ospf_lsa_install() failed", __func__); | |
163 | ospf_lsa_unlock(&lsa); | |
164 | return; | |
165 | } | |
166 | ||
167 | /* Update new LSA origination count. */ | |
168 | oi->ospf->lsa_originate_count++; | |
169 | ||
170 | /* Flood the LSA through out the interface */ | |
171 | ospf_flood_through_interface(oi, NULL, lsa); | |
172 | } | |
173 | ||
10514170 RW |
174 | /* Flush all self-originated Grace-LSAs. */ |
175 | static void ospf_gr_flush_grace_lsas(struct ospf *ospf) | |
176 | { | |
177 | struct ospf_area *area; | |
178 | struct listnode *anode; | |
179 | ||
180 | for (ALL_LIST_ELEMENTS_RO(ospf->areas, anode, area)) { | |
10514170 RW |
181 | struct ospf_interface *oi; |
182 | struct listnode *inode; | |
183 | ||
184 | if (IS_DEBUG_OSPF_GR) | |
185 | zlog_debug( | |
186 | "GR: flushing self-originated Grace-LSAs [area %pI4]", | |
187 | &area->area_id); | |
188 | ||
10514170 | 189 | for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi)) |
4a0167fe | 190 | ospf_gr_lsa_originate(oi, true); |
10514170 RW |
191 | } |
192 | } | |
193 | ||
194 | /* Exit from the Graceful Restart mode. */ | |
195 | static void ospf_gr_restart_exit(struct ospf *ospf, const char *reason) | |
196 | { | |
197 | struct ospf_area *area; | |
198 | struct listnode *onode, *anode; | |
199 | ||
200 | if (IS_DEBUG_OSPF_GR) | |
201 | zlog_debug("GR: exiting graceful restart: %s", reason); | |
202 | ||
203 | ospf->gr_info.restart_in_progress = false; | |
cccd44f3 | 204 | THREAD_OFF(ospf->gr_info.t_grace_period); |
10514170 RW |
205 | |
206 | /* Record in non-volatile memory that the restart is complete. */ | |
207 | ospf_gr_nvm_delete(ospf); | |
208 | ||
209 | for (ALL_LIST_ELEMENTS_RO(ospf->areas, onode, area)) { | |
210 | struct ospf_interface *oi; | |
211 | ||
212 | /* | |
213 | * 1) The router should reoriginate its router-LSAs for all | |
214 | * attached areas in order to make sure they have the correct | |
215 | * contents. | |
216 | */ | |
217 | ospf_router_lsa_update_area(area); | |
218 | ||
219 | /* | |
220 | * 2) The router should reoriginate network-LSAs on all segments | |
221 | * where it is the Designated Router. | |
222 | */ | |
223 | for (ALL_LIST_ELEMENTS_RO(area->oiflist, anode, oi)) | |
224 | if (oi->state == ISM_DR) | |
225 | ospf_network_lsa_update(oi); | |
226 | } | |
227 | ||
228 | /* | |
229 | * 5) Any received self-originated LSAs that are no longer valid should | |
230 | * be flushed. | |
231 | */ | |
232 | ospf_schedule_abr_task(ospf); | |
233 | ||
234 | /* | |
235 | * 3) The router reruns its OSPF routing calculations, this time | |
236 | * installing the results into the system forwarding table, and | |
237 | * originating summary-LSAs, Type-7 LSAs and AS-external-LSAs as | |
238 | * necessary. | |
239 | * | |
240 | * 4) Any remnant entries in the system forwarding table that were | |
241 | * installed before the restart, but that are no longer valid, | |
242 | * should be removed. | |
243 | */ | |
244 | ospf->gr_info.finishing_restart = true; | |
245 | ospf_spf_calculate_schedule(ospf, SPF_FLAG_GR_FINISH); | |
246 | ||
247 | /* 6) Any grace-LSAs that the router originated should be flushed. */ | |
248 | ospf_gr_flush_grace_lsas(ospf); | |
249 | } | |
250 | ||
251 | /* Check if a Router-LSA contains a given link. */ | |
252 | static bool ospf_router_lsa_contains_adj(struct ospf_lsa *lsa, | |
253 | struct in_addr *id) | |
254 | { | |
255 | struct router_lsa *rl; | |
256 | ||
257 | rl = (struct router_lsa *)lsa->data; | |
258 | for (int i = 0; i < ntohs(rl->links); i++) { | |
259 | struct in_addr *link_id = &rl->link[i].link_id; | |
260 | ||
261 | if (rl->link[i].type != LSA_LINK_TYPE_POINTOPOINT) | |
262 | continue; | |
263 | ||
264 | if (IPV4_ADDR_SAME(id, link_id)) | |
265 | return true; | |
266 | } | |
267 | ||
268 | return false; | |
269 | } | |
270 | ||
271 | static bool ospf_gr_check_router_lsa_consistency(struct ospf *ospf, | |
272 | struct ospf_area *area, | |
273 | struct ospf_lsa *lsa) | |
274 | { | |
275 | if (CHECK_FLAG(lsa->flags, OSPF_LSA_SELF)) { | |
276 | struct ospf_lsa *lsa_self = lsa; | |
277 | struct router_lsa *rl = (struct router_lsa *)lsa->data; | |
278 | ||
279 | for (int i = 0; i < ntohs(rl->links); i++) { | |
280 | struct in_addr *link_id = &rl->link[i].link_id; | |
281 | struct ospf_lsa *lsa_adj; | |
282 | ||
283 | if (rl->link[i].type != LSA_LINK_TYPE_POINTOPOINT) | |
284 | continue; | |
285 | ||
286 | lsa_adj = ospf_lsa_lookup_by_id(area, OSPF_ROUTER_LSA, | |
287 | *link_id); | |
288 | if (!lsa_adj) | |
289 | continue; | |
290 | ||
291 | if (!ospf_router_lsa_contains_adj(lsa_adj, | |
292 | &lsa_self->data->id)) | |
293 | return false; | |
294 | } | |
295 | } else { | |
296 | struct ospf_lsa *lsa_self; | |
297 | ||
298 | lsa_self = ospf_lsa_lookup_by_id(area, OSPF_ROUTER_LSA, | |
299 | ospf->router_id); | |
300 | if (!lsa_self | |
301 | || !CHECK_FLAG(lsa_self->flags, OSPF_LSA_RECEIVED)) | |
302 | return true; | |
303 | ||
304 | if (ospf_router_lsa_contains_adj(lsa, &ospf->router_id) | |
305 | != ospf_router_lsa_contains_adj(lsa_self, &lsa->data->id)) | |
306 | return false; | |
307 | } | |
308 | ||
309 | return true; | |
310 | } | |
311 | ||
312 | /* | |
313 | * Check for LSAs that are inconsistent with the pre-restart LSAs, and abort the | |
314 | * ongoing graceful restart when that's the case. | |
315 | */ | |
316 | void ospf_gr_check_lsdb_consistency(struct ospf *ospf, struct ospf_area *area) | |
317 | { | |
318 | struct route_node *rn; | |
319 | struct ospf_lsa *lsa; | |
320 | ||
321 | for (rn = route_top(ROUTER_LSDB(area)); rn; rn = route_next(rn)) { | |
322 | lsa = rn->info; | |
323 | if (!lsa) | |
324 | continue; | |
325 | ||
326 | if (!ospf_gr_check_router_lsa_consistency(ospf, area, lsa)) { | |
327 | char reason[256]; | |
328 | ||
329 | snprintfrr(reason, sizeof(reason), | |
330 | "detected inconsistent LSA[%s] [area %pI4]", | |
331 | dump_lsa_key(lsa), &area->area_id); | |
332 | ospf_gr_restart_exit(ospf, reason); | |
333 | route_unlock_node(rn); | |
334 | return; | |
335 | } | |
336 | } | |
337 | } | |
338 | ||
339 | /* Lookup neighbor by address in a given OSPF area. */ | |
340 | static struct ospf_neighbor * | |
341 | ospf_area_nbr_lookup_by_addr(struct ospf_area *area, struct in_addr *addr) | |
342 | { | |
343 | struct ospf_interface *oi; | |
344 | struct ospf_neighbor *nbr; | |
345 | struct listnode *node; | |
346 | ||
347 | for (ALL_LIST_ELEMENTS_RO(area->oiflist, node, oi)) { | |
348 | nbr = ospf_nbr_lookup_by_addr(oi->nbrs, addr); | |
349 | if (nbr) | |
350 | return nbr; | |
351 | } | |
352 | ||
353 | return NULL; | |
354 | } | |
355 | ||
356 | /* Lookup neighbor by Router ID in a given OSPF area. */ | |
357 | static struct ospf_neighbor * | |
358 | ospf_area_nbr_lookup_by_routerid(struct ospf_area *area, struct in_addr *id) | |
359 | { | |
360 | struct ospf_interface *oi; | |
361 | struct ospf_neighbor *nbr; | |
362 | struct listnode *node; | |
363 | ||
364 | for (ALL_LIST_ELEMENTS_RO(area->oiflist, node, oi)) { | |
365 | nbr = ospf_nbr_lookup_by_routerid(oi->nbrs, id); | |
366 | if (nbr) | |
367 | return nbr; | |
368 | } | |
369 | ||
370 | return NULL; | |
371 | } | |
372 | ||
373 | /* Check if there's a fully formed adjacency with the given neighbor ID. */ | |
374 | static bool ospf_gr_check_adj_id(struct ospf_area *area, | |
375 | struct in_addr *nbr_id) | |
376 | { | |
377 | struct ospf_neighbor *nbr; | |
378 | ||
379 | nbr = ospf_area_nbr_lookup_by_routerid(area, nbr_id); | |
380 | if (!nbr || nbr->state < NSM_Full) { | |
381 | if (IS_DEBUG_OSPF_GR) | |
382 | zlog_debug("GR: missing adjacency to router %pI4", | |
383 | nbr_id); | |
384 | return false; | |
385 | } | |
386 | ||
387 | return true; | |
388 | } | |
389 | ||
390 | static bool ospf_gr_check_adjs_lsa_transit(struct ospf_area *area, | |
391 | struct in_addr *link_id) | |
392 | { | |
393 | struct ospf *ospf = area->ospf; | |
394 | struct ospf_interface *oi; | |
395 | ||
396 | /* | |
397 | * Check if the transit network refers to a local interface (in which | |
398 | * case it must be a DR for that network). | |
399 | */ | |
400 | oi = ospf_if_lookup_by_local_addr(ospf, NULL, *link_id); | |
401 | if (oi) { | |
402 | struct ospf_lsa *lsa; | |
403 | struct network_lsa *nlsa; | |
404 | size_t cnt; | |
405 | ||
406 | /* Lookup Network LSA corresponding to this interface. */ | |
407 | lsa = ospf_lsa_lookup_by_id(area, OSPF_NETWORK_LSA, *link_id); | |
408 | if (!lsa) | |
409 | return false; | |
410 | ||
411 | /* Iterate over all routers present in the network. */ | |
412 | nlsa = (struct network_lsa *)lsa->data; | |
413 | cnt = (lsa->size - (OSPF_LSA_HEADER_SIZE + 4)) / 4; | |
414 | for (size_t i = 0; i < cnt; i++) { | |
415 | struct in_addr *nbr_id = &nlsa->routers[i]; | |
416 | ||
417 | /* Skip self in the pseudonode. */ | |
418 | if (IPV4_ADDR_SAME(nbr_id, &ospf->router_id)) | |
419 | continue; | |
420 | ||
421 | /* | |
422 | * Check if there's a fully formed adjacency with this | |
423 | * router. | |
424 | */ | |
425 | if (!ospf_gr_check_adj_id(area, nbr_id)) | |
426 | return false; | |
427 | } | |
428 | } else { | |
429 | struct ospf_neighbor *nbr; | |
430 | ||
431 | /* Check if there's a fully formed adjacency with the DR. */ | |
432 | nbr = ospf_area_nbr_lookup_by_addr(area, link_id); | |
433 | if (!nbr || nbr->state < NSM_Full) { | |
434 | if (IS_DEBUG_OSPF_GR) | |
435 | zlog_debug( | |
436 | "GR: missing adjacency to DR router %pI4", | |
437 | link_id); | |
438 | return false; | |
439 | } | |
440 | } | |
441 | ||
442 | return true; | |
443 | } | |
444 | ||
445 | static bool ospf_gr_check_adjs_lsa(struct ospf_area *area, struct ospf_lsa *lsa) | |
446 | { | |
447 | struct router_lsa *rl = (struct router_lsa *)lsa->data; | |
448 | ||
449 | for (int i = 0; i < ntohs(rl->links); i++) { | |
450 | struct in_addr *link_id = &rl->link[i].link_id; | |
451 | ||
452 | switch (rl->link[i].type) { | |
453 | case LSA_LINK_TYPE_POINTOPOINT: | |
454 | if (!ospf_gr_check_adj_id(area, link_id)) | |
455 | return false; | |
456 | break; | |
457 | case LSA_LINK_TYPE_TRANSIT: | |
458 | if (!ospf_gr_check_adjs_lsa_transit(area, link_id)) | |
459 | return false; | |
460 | break; | |
461 | default: | |
462 | break; | |
463 | } | |
464 | } | |
465 | ||
466 | return true; | |
467 | } | |
468 | ||
469 | /* | |
470 | * Check if all adjacencies prior to the restart were reestablished. | |
471 | * | |
472 | * This is done using pre-restart Router LSAs and pre-restart Network LSAs | |
473 | * received from the helping neighbors. | |
474 | */ | |
475 | void ospf_gr_check_adjs(struct ospf *ospf) | |
476 | { | |
477 | struct ospf_area *area; | |
478 | struct listnode *node; | |
479 | ||
480 | for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { | |
481 | struct ospf_lsa *lsa_self; | |
482 | ||
483 | lsa_self = ospf_lsa_lookup_by_id(area, OSPF_ROUTER_LSA, | |
484 | ospf->router_id); | |
485 | if (!lsa_self || !ospf_gr_check_adjs_lsa(area, lsa_self)) { | |
486 | if (IS_DEBUG_OSPF_GR) | |
487 | zlog_debug( | |
488 | "GR: not all adjacencies were reestablished yet [area %pI4]", | |
489 | &area->area_id); | |
490 | return; | |
491 | } | |
492 | } | |
493 | ||
494 | ospf_gr_restart_exit(ospf, "all adjacencies were reestablished"); | |
495 | } | |
496 | ||
497 | /* Handling of grace period expiry. */ | |
cc9f21da | 498 | static void ospf_gr_grace_period_expired(struct thread *thread) |
10514170 RW |
499 | { |
500 | struct ospf *ospf = THREAD_ARG(thread); | |
501 | ||
502 | ospf->gr_info.t_grace_period = NULL; | |
503 | ospf_gr_restart_exit(ospf, "grace period has expired"); | |
10514170 RW |
504 | } |
505 | ||
506 | /* | |
507 | * Returns the path of the file (non-volatile memory) that contains GR status | |
508 | * information. | |
509 | */ | |
510 | static char *ospf_gr_nvm_filepath(struct ospf *ospf) | |
511 | { | |
512 | static char filepath[MAXPATHLEN]; | |
513 | char instance[16] = ""; | |
514 | ||
515 | if (ospf->instance) | |
516 | snprintf(instance, sizeof(instance), "-%d", ospf->instance); | |
517 | snprintf(filepath, sizeof(filepath), OSPFD_GR_STATE, instance); | |
518 | return filepath; | |
519 | } | |
520 | ||
521 | /* | |
522 | * Record in non-volatile memory that the given OSPF instance is attempting to | |
523 | * perform a graceful restart. | |
524 | */ | |
525 | static void ospf_gr_nvm_update(struct ospf *ospf) | |
526 | { | |
527 | char *filepath; | |
528 | const char *inst_name; | |
529 | json_object *json; | |
530 | json_object *json_instances; | |
531 | json_object *json_instance; | |
532 | ||
533 | filepath = ospf_gr_nvm_filepath(ospf); | |
44076f4d | 534 | inst_name = ospf_get_name(ospf); |
10514170 RW |
535 | |
536 | json = json_object_from_file(filepath); | |
537 | if (json == NULL) | |
538 | json = json_object_new_object(); | |
539 | ||
540 | json_object_object_get_ex(json, "instances", &json_instances); | |
541 | if (!json_instances) { | |
542 | json_instances = json_object_new_object(); | |
543 | json_object_object_add(json, "instances", json_instances); | |
544 | } | |
545 | ||
546 | json_object_object_get_ex(json_instances, inst_name, &json_instance); | |
547 | if (!json_instance) { | |
548 | json_instance = json_object_new_object(); | |
549 | json_object_object_add(json_instances, inst_name, | |
550 | json_instance); | |
551 | } | |
552 | ||
553 | /* | |
554 | * Record not only the grace period, but also a UNIX timestamp | |
555 | * corresponding to the end of that period. That way, once ospfd is | |
556 | * restarted, it will be possible to take into account the time that | |
557 | * passed while ospfd wasn't running. | |
558 | */ | |
559 | json_object_int_add(json_instance, "gracePeriod", | |
560 | ospf->gr_info.grace_period); | |
561 | json_object_int_add(json_instance, "timestamp", | |
562 | time(NULL) + ospf->gr_info.grace_period); | |
563 | ||
564 | json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY); | |
565 | json_object_free(json); | |
566 | } | |
567 | ||
568 | /* | |
569 | * Delete GR status information about the given OSPF instance from non-volatile | |
570 | * memory. | |
571 | */ | |
572 | static void ospf_gr_nvm_delete(struct ospf *ospf) | |
573 | { | |
574 | char *filepath; | |
575 | const char *inst_name; | |
576 | json_object *json; | |
577 | json_object *json_instances; | |
578 | ||
579 | filepath = ospf_gr_nvm_filepath(ospf); | |
44076f4d | 580 | inst_name = ospf_get_name(ospf); |
10514170 RW |
581 | |
582 | json = json_object_from_file(filepath); | |
583 | if (json == NULL) | |
584 | json = json_object_new_object(); | |
585 | ||
586 | json_object_object_get_ex(json, "instances", &json_instances); | |
587 | if (!json_instances) { | |
588 | json_instances = json_object_new_object(); | |
589 | json_object_object_add(json, "instances", json_instances); | |
590 | } | |
591 | ||
592 | json_object_object_del(json_instances, inst_name); | |
593 | ||
594 | json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY); | |
595 | json_object_free(json); | |
596 | } | |
597 | ||
598 | /* | |
599 | * Fetch from non-volatile memory whether the given OSPF instance is performing | |
600 | * a graceful shutdown or not. | |
601 | */ | |
602 | void ospf_gr_nvm_read(struct ospf *ospf) | |
603 | { | |
604 | char *filepath; | |
605 | const char *inst_name; | |
606 | json_object *json; | |
607 | json_object *json_instances; | |
608 | json_object *json_instance; | |
609 | json_object *json_timestamp; | |
610 | time_t timestamp = 0; | |
611 | ||
612 | filepath = ospf_gr_nvm_filepath(ospf); | |
44076f4d | 613 | inst_name = ospf_get_name(ospf); |
10514170 RW |
614 | |
615 | json = json_object_from_file(filepath); | |
616 | if (json == NULL) | |
617 | json = json_object_new_object(); | |
618 | ||
619 | json_object_object_get_ex(json, "instances", &json_instances); | |
620 | if (!json_instances) { | |
621 | json_instances = json_object_new_object(); | |
622 | json_object_object_add(json, "instances", json_instances); | |
623 | } | |
624 | ||
625 | json_object_object_get_ex(json_instances, inst_name, &json_instance); | |
626 | if (!json_instance) { | |
627 | json_instance = json_object_new_object(); | |
628 | json_object_object_add(json_instances, inst_name, | |
629 | json_instance); | |
630 | } | |
631 | ||
632 | json_object_object_get_ex(json_instance, "timestamp", &json_timestamp); | |
633 | if (json_timestamp) { | |
634 | time_t now; | |
635 | unsigned long remaining_time; | |
636 | ||
637 | /* Check if the grace period has already expired. */ | |
638 | now = time(NULL); | |
639 | timestamp = json_object_get_int(json_timestamp); | |
640 | if (now > timestamp) { | |
641 | ospf_gr_restart_exit( | |
642 | ospf, "grace period has expired already"); | |
643 | } else { | |
644 | /* Schedule grace period timeout. */ | |
645 | ospf->gr_info.restart_in_progress = true; | |
646 | remaining_time = timestamp - time(NULL); | |
647 | if (IS_DEBUG_OSPF_GR) | |
648 | zlog_debug( | |
649 | "GR: remaining time until grace period expires: %lu(s)", | |
650 | remaining_time); | |
651 | thread_add_timer(master, ospf_gr_grace_period_expired, | |
652 | ospf, remaining_time, | |
653 | &ospf->gr_info.t_grace_period); | |
654 | } | |
655 | } | |
656 | ||
657 | json_object_object_del(json_instances, inst_name); | |
658 | ||
659 | json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY); | |
660 | json_object_free(json); | |
661 | } | |
662 | ||
663 | /* Prepare to start a Graceful Restart. */ | |
664 | static void ospf_gr_prepare(void) | |
665 | { | |
666 | struct ospf *ospf; | |
667 | struct ospf_interface *oi; | |
668 | struct listnode *onode; | |
669 | ||
670 | for (ALL_LIST_ELEMENTS_RO(om->ospf, onode, ospf)) { | |
671 | struct listnode *inode; | |
672 | ||
673 | if (!ospf->gr_info.restart_support | |
674 | || ospf->gr_info.prepare_in_progress) | |
675 | continue; | |
676 | ||
677 | if (IS_DEBUG_OSPF_GR) | |
678 | zlog_debug( | |
679 | "GR: preparing to perform a graceful restart [period %u second(s)] [vrf %s]", | |
680 | ospf->gr_info.grace_period, | |
681 | ospf_vrf_id_to_name(ospf->vrf_id)); | |
682 | ||
683 | if (!CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE)) { | |
684 | zlog_warn( | |
685 | "%s: failed to activate graceful restart: opaque capability not enabled", | |
686 | __func__); | |
687 | continue; | |
688 | } | |
689 | ||
690 | /* Freeze OSPF routes in the RIB. */ | |
691 | if (ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period)) { | |
692 | zlog_warn( | |
693 | "%s: failed to activate graceful restart: not connected to zebra", | |
694 | __func__); | |
695 | continue; | |
696 | } | |
697 | ||
698 | /* Send a Grace-LSA to all neighbors. */ | |
699 | for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, inode, oi)) | |
4a0167fe | 700 | ospf_gr_lsa_originate(oi, false); |
10514170 RW |
701 | |
702 | /* Record end of the grace period in non-volatile memory. */ | |
703 | ospf_gr_nvm_update(ospf); | |
704 | ||
705 | /* | |
706 | * Mark that a Graceful Restart preparation is in progress, to | |
707 | * prevent ospfd from flushing its self-originated LSAs on exit. | |
708 | */ | |
709 | ospf->gr_info.prepare_in_progress = true; | |
710 | } | |
711 | } | |
712 | ||
713 | DEFPY(graceful_restart_prepare, graceful_restart_prepare_cmd, | |
714 | "graceful-restart prepare ip ospf", | |
715 | "Graceful Restart commands\n" | |
716 | "Prepare upcoming graceful restart\n" | |
717 | IP_STR | |
4a3c92de | 718 | "Prepare to restart the OSPF process\n") |
10514170 | 719 | { |
eedc80c1 RW |
720 | struct ospf *ospf; |
721 | struct listnode *node; | |
722 | ||
723 | for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { | |
724 | if (!CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE)) { | |
725 | vty_out(vty, | |
726 | "%% Can't start graceful restart: opaque capability not enabled (VRF %s)\n\n", | |
727 | ospf_get_name(ospf)); | |
728 | return CMD_WARNING; | |
729 | } | |
730 | } | |
731 | ||
10514170 RW |
732 | ospf_gr_prepare(); |
733 | ||
734 | return CMD_SUCCESS; | |
735 | } | |
736 | ||
737 | DEFPY(graceful_restart, graceful_restart_cmd, | |
738 | "graceful-restart [grace-period (1-1800)$grace_period]", | |
739 | OSPF_GR_STR | |
740 | "Maximum length of the 'grace period'\n" | |
741 | "Maximum length of the 'grace period' in seconds\n") | |
742 | { | |
743 | VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); | |
744 | ||
745 | /* Check and get restart period if present. */ | |
746 | if (!grace_period_str) | |
747 | grace_period = OSPF_DFLT_GRACE_INTERVAL; | |
748 | ||
749 | ospf->gr_info.restart_support = true; | |
750 | ospf->gr_info.grace_period = grace_period; | |
751 | ||
752 | return CMD_SUCCESS; | |
753 | } | |
754 | ||
755 | DEFPY(no_graceful_restart, no_graceful_restart_cmd, | |
9a8c0f2d | 756 | "no graceful-restart [grace-period (1-1800)]", |
10514170 RW |
757 | NO_STR OSPF_GR_STR |
758 | "Maximum length of the 'grace period'\n" | |
759 | "Maximum length of the 'grace period' in seconds\n") | |
760 | { | |
761 | VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); | |
762 | ||
763 | if (!ospf->gr_info.restart_support) | |
764 | return CMD_SUCCESS; | |
765 | ||
766 | if (ospf->gr_info.prepare_in_progress) { | |
767 | vty_out(vty, | |
768 | "%% Error: Graceful Restart preparation in progress\n"); | |
769 | return CMD_WARNING; | |
770 | } | |
771 | ||
772 | ospf->gr_info.restart_support = false; | |
773 | ospf->gr_info.grace_period = OSPF_DFLT_GRACE_INTERVAL; | |
774 | ||
775 | return CMD_SUCCESS; | |
776 | } | |
777 | ||
778 | void ospf_gr_init(void) | |
779 | { | |
780 | install_element(ENABLE_NODE, &graceful_restart_prepare_cmd); | |
781 | install_element(OSPF_NODE, &graceful_restart_cmd); | |
782 | install_element(OSPF_NODE, &no_graceful_restart_cmd); | |
783 | } |