]> git.proxmox.com Git - mirror_frr.git/blame - ospf6d/ospf6_gr.c
ospf6d: fix refreshing of NSSA/AS-external LSAs after a graceful restart
[mirror_frr.git] / ospf6d / ospf6_gr.c
CommitLineData
acddc0ed 1// SPDX-License-Identifier: GPL-2.0-or-later
71165098
RW
2/*
3 * This is an implementation of RFC 5187 Graceful Restart.
4 *
5 * Copyright 2021 NetDEF (c), All rights reserved.
71165098
RW
6 */
7
8#include <zebra.h>
9
10#include "memory.h"
11#include "command.h"
12#include "table.h"
13#include "vty.h"
14#include "log.h"
15#include "hook.h"
16#include "printfrr.h"
17
18#include "ospf6d/ospf6_lsa.h"
19#include "ospf6d/ospf6_lsdb.h"
20#include "ospf6d/ospf6_route.h"
21#include "ospf6d/ospf6_area.h"
22#include "ospf6d/ospf6_interface.h"
23#include "ospf6d/ospf6d.h"
24#include "ospf6d/ospf6_asbr.h"
25#include "ospf6d/ospf6_zebra.h"
26#include "ospf6d/ospf6_message.h"
27#include "ospf6d/ospf6_neighbor.h"
28#include "ospf6d/ospf6_flood.h"
29#include "ospf6d/ospf6_intra.h"
30#include "ospf6d/ospf6_spf.h"
31#include "ospf6d/ospf6_gr.h"
71165098 32#include "ospf6d/ospf6_gr_clippy.c"
71165098
RW
33
34static void ospf6_gr_nvm_delete(struct ospf6 *ospf6);
35
36/* Originate and install Grace-LSA for a given interface. */
37static int ospf6_gr_lsa_originate(struct ospf6_interface *oi)
38{
39 struct ospf6_gr_info *gr_info = &oi->area->ospf6->gr_info;
40 struct ospf6_lsa_header *lsa_header;
41 struct ospf6_grace_lsa *grace_lsa;
42 struct ospf6_lsa *lsa;
43 char buffer[OSPF6_MAX_LSASIZE];
44
45 if (IS_OSPF6_DEBUG_ORIGINATE(LINK))
d6f60d22 46 zlog_debug("Originate Grace-LSA for Interface %s",
71165098
RW
47 oi->interface->name);
48
49 /* prepare buffer */
50 memset(buffer, 0, sizeof(buffer));
51 lsa_header = (struct ospf6_lsa_header *)buffer;
52 grace_lsa =
53 (struct ospf6_grace_lsa *)((caddr_t)lsa_header
54 + sizeof(struct ospf6_lsa_header));
55
56 /* Put grace period. */
57 grace_lsa->tlv_period.header.type = htons(GRACE_PERIOD_TYPE);
58 grace_lsa->tlv_period.header.length = htons(GRACE_PERIOD_LENGTH);
59 grace_lsa->tlv_period.interval = htonl(gr_info->grace_period);
60
61 /* Put restart reason. */
62 grace_lsa->tlv_reason.header.type = htons(RESTART_REASON_TYPE);
63 grace_lsa->tlv_reason.header.length = htons(RESTART_REASON_LENGTH);
64 if (gr_info->restart_support)
65 grace_lsa->tlv_reason.reason = OSPF6_GR_SW_RESTART;
66 else
67 grace_lsa->tlv_reason.reason = OSPF6_GR_UNKNOWN_RESTART;
68
69 /* Fill LSA Header */
70 lsa_header->age = 0;
71 lsa_header->type = htons(OSPF6_LSTYPE_GRACE_LSA);
72 lsa_header->id = htonl(oi->interface->ifindex);
73 lsa_header->adv_router = oi->area->ospf6->router_id;
74 lsa_header->seqnum =
75 ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id,
76 lsa_header->adv_router, oi->lsdb);
77 lsa_header->length = htons(sizeof(*lsa_header) + sizeof(*grace_lsa));
78
79 /* LSA checksum */
80 ospf6_lsa_checksum(lsa_header);
81
82 /* create LSA */
83 lsa = ospf6_lsa_create(lsa_header);
84
85 /* Originate */
86 ospf6_lsa_originate_interface(lsa, oi);
87
88 return 0;
89}
90
91/* Flush all self-originated Grace-LSAs. */
92static void ospf6_gr_flush_grace_lsas(struct ospf6 *ospf6)
93{
94 struct ospf6_area *area;
95 struct listnode *anode;
96
97 for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, area)) {
98 struct ospf6_lsa *lsa;
99 struct ospf6_interface *oi;
100 struct listnode *inode;
101
102 if (IS_DEBUG_OSPF6_GR)
103 zlog_debug(
104 "GR: flushing self-originated Grace-LSAs [area %pI4]",
105 &area->area_id);
106
107 for (ALL_LIST_ELEMENTS_RO(area->if_list, inode, oi)) {
108 lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_GRACE_LSA),
109 htonl(oi->interface->ifindex),
110 oi->area->ospf6->router_id,
111 oi->lsdb);
112 if (!lsa) {
113 zlog_warn(
114 "%s: Grace-LSA not found [interface %s] [area %pI4]",
115 __func__, oi->interface->name,
116 &area->area_id);
117 continue;
118 }
119
120 ospf6_lsa_purge(lsa);
121 }
122 }
123}
124
125/* Exit from the Graceful Restart mode. */
126static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason)
127{
128 struct ospf6_area *area;
129 struct listnode *onode, *anode;
5eb2c602 130 struct ospf6_route *route;
71165098
RW
131
132 if (IS_DEBUG_OSPF6_GR)
133 zlog_debug("GR: exiting graceful restart: %s", reason);
134
135 ospf6->gr_info.restart_in_progress = false;
136 ospf6->gr_info.finishing_restart = true;
137 THREAD_OFF(ospf6->gr_info.t_grace_period);
138
139 /* Record in non-volatile memory that the restart is complete. */
140 ospf6_gr_nvm_delete(ospf6);
141
142 for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, onode, area)) {
143 struct ospf6_interface *oi;
144
145 /*
146 * 1) The router should reoriginate its router-LSAs for all
147 * attached areas in order to make sure they have the correct
148 * contents.
149 */
150 OSPF6_ROUTER_LSA_EXECUTE(area);
151
152 for (ALL_LIST_ELEMENTS_RO(area->if_list, anode, oi)) {
5cbcc459
RW
153 /* Reoriginate Link-LSA. */
154 if (oi->type != OSPF_IFTYPE_VIRTUALLINK)
155 OSPF6_LINK_LSA_EXECUTE(oi);
71165098
RW
156
157 /*
158 * 2) The router should reoriginate network-LSAs on all
159 * segments where it is the Designated Router.
160 */
161 if (oi->state == OSPF6_INTERFACE_DR)
162 OSPF6_NETWORK_LSA_EXECUTE(oi);
163 }
164 }
165
5eb2c602
RW
166 /*
167 * While all self-originated NSSA and AS-external LSAs were already
168 * learned from the helping neighbors, we need to reoriginate them in
169 * order to ensure they will be refreshed periodically.
170 */
171 for (route = ospf6_route_head(ospf6->external_table); route;
172 route = ospf6_route_next(route))
173 ospf6_handle_external_lsa_origination(ospf6, route,
174 &route->prefix);
175
71165098
RW
176 /*
177 * 3) The router reruns its OSPF routing calculations, this time
178 * installing the results into the system forwarding table, and
179 * originating summary-LSAs, Type-7 LSAs and AS-external-LSAs as
180 * necessary.
181 *
182 * 4) Any remnant entries in the system forwarding table that were
183 * installed before the restart, but that are no longer valid,
184 * should be removed.
185 */
186 ospf6_spf_schedule(ospf6, OSPF6_SPF_FLAGS_GR_FINISH);
187
188 /* 6) Any grace-LSAs that the router originated should be flushed. */
189 ospf6_gr_flush_grace_lsas(ospf6);
190}
191
192#define RTR_LSA_MISSING 0
193#define RTR_LSA_ADJ_FOUND 1
194#define RTR_LSA_ADJ_NOT_FOUND 2
195
196/* Check if a Router-LSA exists and if it contains a given link. */
197static int ospf6_router_lsa_contains_adj(struct ospf6_area *area,
198 in_addr_t adv_router,
199 in_addr_t neighbor_router_id)
200{
201 uint16_t type;
202 struct ospf6_lsa *lsa;
203 bool empty = true;
204
205 type = ntohs(OSPF6_LSTYPE_ROUTER);
206 for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, adv_router, lsa)) {
207 struct ospf6_router_lsa *router_lsa;
208 char *start, *end, *current;
209
210 empty = false;
211 router_lsa = (struct ospf6_router_lsa
212 *)((char *)lsa->header
213 + sizeof(struct ospf6_lsa_header));
214
215 /* Iterate over all interfaces in the Router-LSA. */
216 start = (char *)router_lsa + sizeof(struct ospf6_router_lsa);
217 end = (char *)lsa->header + ntohs(lsa->header->length);
218 for (current = start;
219 current + sizeof(struct ospf6_router_lsdesc) <= end;
220 current += sizeof(struct ospf6_router_lsdesc)) {
221 struct ospf6_router_lsdesc *lsdesc;
222
223 lsdesc = (struct ospf6_router_lsdesc *)current;
224 if (lsdesc->type != OSPF6_ROUTER_LSDESC_POINTTOPOINT)
225 continue;
226
227 if (lsdesc->neighbor_router_id == neighbor_router_id)
228 return RTR_LSA_ADJ_FOUND;
229 }
230 }
231
232 if (empty)
233 return RTR_LSA_MISSING;
234
235 return RTR_LSA_ADJ_NOT_FOUND;
236}
237
238static bool ospf6_gr_check_router_lsa_consistency(struct ospf6 *ospf6,
239 struct ospf6_area *area,
240 struct ospf6_lsa *lsa)
241{
242 if (lsa->header->adv_router == ospf6->router_id) {
243 struct ospf6_router_lsa *router_lsa;
244 char *start, *end, *current;
245
246 router_lsa = (struct ospf6_router_lsa
247 *)((char *)lsa->header
248 + sizeof(struct ospf6_lsa_header));
249
250 /* Iterate over all interfaces in the Router-LSA. */
251 start = (char *)router_lsa + sizeof(struct ospf6_router_lsa);
252 end = (char *)lsa->header + ntohs(lsa->header->length);
253 for (current = start;
254 current + sizeof(struct ospf6_router_lsdesc) <= end;
255 current += sizeof(struct ospf6_router_lsdesc)) {
256 struct ospf6_router_lsdesc *lsdesc;
257
258 lsdesc = (struct ospf6_router_lsdesc *)current;
259 if (lsdesc->type != OSPF6_ROUTER_LSDESC_POINTTOPOINT)
260 continue;
261
262 if (ospf6_router_lsa_contains_adj(
263 area, lsdesc->neighbor_router_id,
264 ospf6->router_id)
265 == RTR_LSA_ADJ_NOT_FOUND)
266 return false;
267 }
268 } else {
269 int adj1, adj2;
270
271 adj1 = ospf6_router_lsa_contains_adj(area, ospf6->router_id,
272 lsa->header->adv_router);
273 adj2 = ospf6_router_lsa_contains_adj(
274 area, lsa->header->adv_router, ospf6->router_id);
275 if ((adj1 == RTR_LSA_ADJ_FOUND && adj2 == RTR_LSA_ADJ_NOT_FOUND)
276 || (adj1 == RTR_LSA_ADJ_NOT_FOUND
277 && adj2 == RTR_LSA_ADJ_FOUND))
278 return false;
279 }
280
281 return true;
282}
283
284/*
285 * Check for LSAs that are inconsistent with the pre-restart LSAs, and abort the
286 * ongoing graceful restart when that's the case.
287 */
288void ospf6_gr_check_lsdb_consistency(struct ospf6 *ospf6,
289 struct ospf6_area *area)
290{
291 uint16_t type;
292 struct ospf6_lsa *lsa;
293
294 type = ntohs(OSPF6_LSTYPE_ROUTER);
295 for (ALL_LSDB_TYPED(area->lsdb, type, lsa)) {
296 if (!ospf6_gr_check_router_lsa_consistency(ospf6, area, lsa)) {
297 char reason[256];
298
299 snprintfrr(reason, sizeof(reason),
300 "detected inconsistent LSA %s [area %pI4]",
301 lsa->name, &area->area_id);
302 ospf6_gr_restart_exit(ospf6, reason);
303 return;
304 }
305 }
306}
307
308/* Check if there's a fully formed adjacency with the given neighbor ID. */
309static bool ospf6_gr_check_adj_id(struct ospf6_area *area,
310 in_addr_t neighbor_router_id)
311{
312 struct ospf6_neighbor *nbr;
313
314 nbr = ospf6_area_neighbor_lookup(area, neighbor_router_id);
315 if (!nbr || nbr->state < OSPF6_NEIGHBOR_FULL) {
316 if (IS_DEBUG_OSPF6_GR)
317 zlog_debug("GR: missing adjacency to router %pI4",
318 &neighbor_router_id);
319 return false;
320 }
321
322 return true;
323}
324
325static bool ospf6_gr_check_adjs_lsa_transit(struct ospf6_area *area,
326 in_addr_t neighbor_router_id,
327 uint32_t neighbor_interface_id)
328{
329 struct ospf6 *ospf6 = area->ospf6;
330
331 /* Check if we are the DR. */
332 if (neighbor_router_id == ospf6->router_id) {
333 struct ospf6_lsa *lsa;
334 char *start, *end, *current;
335 struct ospf6_network_lsa *network_lsa;
336 struct ospf6_network_lsdesc *lsdesc;
337
338 /* Lookup Network LSA corresponding to this interface. */
339 lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_NETWORK),
340 neighbor_interface_id,
341 neighbor_router_id, area->lsdb);
342 if (!lsa)
343 return false;
344
345 /* Iterate over all routers present in the network. */
346 network_lsa = (struct ospf6_network_lsa
347 *)((char *)lsa->header
348 + sizeof(struct ospf6_lsa_header));
349 start = (char *)network_lsa + sizeof(struct ospf6_network_lsa);
350 end = (char *)lsa->header + ntohs(lsa->header->length);
351 for (current = start;
352 current + sizeof(struct ospf6_network_lsdesc) <= end;
353 current += sizeof(struct ospf6_network_lsdesc)) {
354 lsdesc = (struct ospf6_network_lsdesc *)current;
355
356 /* Skip self in the pseudonode. */
357 if (lsdesc->router_id == ospf6->router_id)
358 continue;
359
360 /*
361 * Check if there's a fully formed adjacency with this
362 * router.
363 */
364 if (!ospf6_gr_check_adj_id(area, lsdesc->router_id))
365 return false;
366 }
367 } else {
368 struct ospf6_neighbor *nbr;
369
370 /* Check if there's a fully formed adjacency with the DR. */
371 nbr = ospf6_area_neighbor_lookup(area, neighbor_router_id);
372 if (!nbr || nbr->state < OSPF6_NEIGHBOR_FULL) {
373 if (IS_DEBUG_OSPF6_GR)
374 zlog_debug(
375 "GR: missing adjacency to DR router %pI4",
376 &neighbor_router_id);
377 return false;
378 }
379 }
380
381 return true;
382}
383
384static bool ospf6_gr_check_adjs_lsa(struct ospf6_area *area,
385 struct ospf6_lsa *lsa)
386{
387 struct ospf6_router_lsa *router_lsa;
388 char *start, *end, *current;
389
390 router_lsa =
391 (struct ospf6_router_lsa *)((char *)lsa->header
392 + sizeof(struct ospf6_lsa_header));
393
394 /* Iterate over all interfaces in the Router-LSA. */
395 start = (char *)router_lsa + sizeof(struct ospf6_router_lsa);
396 end = (char *)lsa->header + ntohs(lsa->header->length);
397 for (current = start;
398 current + sizeof(struct ospf6_router_lsdesc) <= end;
399 current += sizeof(struct ospf6_router_lsdesc)) {
400 struct ospf6_router_lsdesc *lsdesc;
401
402 lsdesc = (struct ospf6_router_lsdesc *)current;
403 switch (lsdesc->type) {
404 case OSPF6_ROUTER_LSDESC_POINTTOPOINT:
405 if (!ospf6_gr_check_adj_id(area,
406 lsdesc->neighbor_router_id))
407 return false;
408 break;
409 case OSPF6_ROUTER_LSDESC_TRANSIT_NETWORK:
410 if (!ospf6_gr_check_adjs_lsa_transit(
411 area, lsdesc->neighbor_router_id,
412 lsdesc->neighbor_interface_id))
413 return false;
414 break;
415 default:
416 break;
417 }
418 }
419
420 return true;
421}
422
423/*
424 * Check if all adjacencies prior to the restart were reestablished.
425 *
426 * This is done using pre-restart Router LSAs and pre-restart Network LSAs
427 * received from the helping neighbors.
428 */
429static bool ospf6_gr_check_adjs(struct ospf6 *ospf6)
430{
431 struct ospf6_area *area;
432 struct listnode *node;
433
434 for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, area)) {
435 uint16_t type;
436 uint32_t router;
437 struct ospf6_lsa *lsa_self;
438 bool found = false;
439
440 type = ntohs(OSPF6_LSTYPE_ROUTER);
441 router = ospf6->router_id;
442 for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, router,
443 lsa_self)) {
444 found = true;
445 if (!ospf6_gr_check_adjs_lsa(area, lsa_self))
446 return false;
447 }
448 if (!found)
449 return false;
450 }
451
452 return true;
453}
454
455/* Handling of grace period expiry. */
cc9f21da 456static void ospf6_gr_grace_period_expired(struct thread *thread)
71165098
RW
457{
458 struct ospf6 *ospf6 = THREAD_ARG(thread);
459
71165098 460 ospf6_gr_restart_exit(ospf6, "grace period has expired");
71165098
RW
461}
462
463/*
464 * Record in non-volatile memory that the given OSPF instance is attempting to
465 * perform a graceful restart.
466 */
467static void ospf6_gr_nvm_update(struct ospf6 *ospf6)
468{
469 const char *inst_name;
470 json_object *json;
471 json_object *json_instances;
472 json_object *json_instance;
473
474 inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME;
475
b275f44a 476 json = json_object_from_file((char *)OSPF6D_GR_STATE);
71165098
RW
477 if (json == NULL)
478 json = json_object_new_object();
479
480 json_object_object_get_ex(json, "instances", &json_instances);
481 if (!json_instances) {
482 json_instances = json_object_new_object();
483 json_object_object_add(json, "instances", json_instances);
484 }
485
486 json_object_object_get_ex(json_instances, inst_name, &json_instance);
487 if (!json_instance) {
488 json_instance = json_object_new_object();
489 json_object_object_add(json_instances, inst_name,
490 json_instance);
491 }
492
493 /*
494 * Record not only the grace period, but also a UNIX timestamp
495 * corresponding to the end of that period. That way, once ospf6d is
496 * restarted, it will be possible to take into account the time that
497 * passed while ospf6d wasn't running.
498 */
499 json_object_int_add(json_instance, "gracePeriod",
500 ospf6->gr_info.grace_period);
501 json_object_int_add(json_instance, "timestamp",
502 time(NULL) + ospf6->gr_info.grace_period);
503
b275f44a
RW
504 json_object_to_file_ext((char *)OSPF6D_GR_STATE, json,
505 JSON_C_TO_STRING_PRETTY);
71165098
RW
506 json_object_free(json);
507}
508
509/*
510 * Delete GR status information about the given OSPF instance from non-volatile
511 * memory.
512 */
513static void ospf6_gr_nvm_delete(struct ospf6 *ospf6)
514{
515 const char *inst_name;
516 json_object *json;
517 json_object *json_instances;
518
519 inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME;
520
b275f44a 521 json = json_object_from_file((char *)OSPF6D_GR_STATE);
71165098
RW
522 if (json == NULL)
523 json = json_object_new_object();
524
525 json_object_object_get_ex(json, "instances", &json_instances);
526 if (!json_instances) {
527 json_instances = json_object_new_object();
528 json_object_object_add(json, "instances", json_instances);
529 }
530
531 json_object_object_del(json_instances, inst_name);
532
b275f44a
RW
533 json_object_to_file_ext((char *)OSPF6D_GR_STATE, json,
534 JSON_C_TO_STRING_PRETTY);
71165098
RW
535 json_object_free(json);
536}
537
538/*
539 * Fetch from non-volatile memory whether the given OSPF instance is performing
540 * a graceful shutdown or not.
541 */
542void ospf6_gr_nvm_read(struct ospf6 *ospf6)
543{
544 const char *inst_name;
545 json_object *json;
546 json_object *json_instances;
547 json_object *json_instance;
548 json_object *json_timestamp;
549 time_t timestamp = 0;
550
551 inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME;
552
b275f44a 553 json = json_object_from_file((char *)OSPF6D_GR_STATE);
71165098
RW
554 if (json == NULL)
555 json = json_object_new_object();
556
557 json_object_object_get_ex(json, "instances", &json_instances);
558 if (!json_instances) {
559 json_instances = json_object_new_object();
560 json_object_object_add(json, "instances", json_instances);
561 }
562
563 json_object_object_get_ex(json_instances, inst_name, &json_instance);
564 if (!json_instance) {
565 json_instance = json_object_new_object();
566 json_object_object_add(json_instances, inst_name,
567 json_instance);
568 }
569
570 json_object_object_get_ex(json_instance, "timestamp", &json_timestamp);
571 if (json_timestamp) {
572 time_t now;
573 unsigned long remaining_time;
574
575 /* Check if the grace period has already expired. */
576 now = time(NULL);
577 timestamp = json_object_get_int(json_timestamp);
578 if (now > timestamp) {
579 ospf6_gr_restart_exit(
580 ospf6, "grace period has expired already");
581 } else {
582 /* Schedule grace period timeout. */
583 ospf6->gr_info.restart_in_progress = true;
584 remaining_time = timestamp - time(NULL);
585 if (IS_DEBUG_OSPF6_GR)
586 zlog_debug(
587 "GR: remaining time until grace period expires: %lu(s)",
588 remaining_time);
589 thread_add_timer(master, ospf6_gr_grace_period_expired,
590 ospf6, remaining_time,
591 &ospf6->gr_info.t_grace_period);
592 }
593 }
594
595 json_object_object_del(json_instances, inst_name);
596
b275f44a
RW
597 json_object_to_file_ext((char *)OSPF6D_GR_STATE, json,
598 JSON_C_TO_STRING_PRETTY);
71165098
RW
599 json_object_free(json);
600}
601
602/* Prepare to start a Graceful Restart. */
603static void ospf6_gr_prepare(void)
604{
605 struct ospf6 *ospf6;
606 struct ospf6_interface *oi;
607 struct listnode *onode, *anode, *inode;
608
609 for (ALL_LIST_ELEMENTS_RO(om6->ospf6, onode, ospf6)) {
610 struct ospf6_area *area;
611
612 if (!ospf6->gr_info.restart_support
613 || ospf6->gr_info.prepare_in_progress)
614 continue;
615
616 if (IS_DEBUG_OSPF6_GR)
617 zlog_debug(
618 "GR: preparing to perform a graceful restart [period %u second(s)] [vrf %s]",
619 ospf6->gr_info.grace_period,
620 ospf6_vrf_id_to_name(ospf6->vrf_id));
621
622 /* Freeze OSPF routes in the RIB. */
623 if (ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period)) {
624 zlog_warn(
625 "%s: failed to activate graceful restart: not connected to zebra",
626 __func__);
627 continue;
628 }
629
630 /* Send a Grace-LSA to all neighbors. */
631 for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, area)) {
632 for (ALL_LIST_ELEMENTS_RO(area->if_list, inode, oi)) {
633 if (oi->state < OSPF6_INTERFACE_POINTTOPOINT)
634 continue;
635 ospf6_gr_lsa_originate(oi);
636 }
637 }
638
639 /* Record end of the grace period in non-volatile memory. */
640 ospf6_gr_nvm_update(ospf6);
641
642 /*
643 * Mark that a Graceful Restart preparation is in progress, to
644 * prevent ospf6d from flushing its self-originated LSAs on
645 * exit.
646 */
647 ospf6->gr_info.prepare_in_progress = true;
648 }
649}
650
651static int ospf6_gr_neighbor_change(struct ospf6_neighbor *on, int next_state,
652 int prev_state)
653{
654 struct ospf6 *ospf6 = on->ospf6_if->area->ospf6;
655
656 if (next_state == OSPF6_NEIGHBOR_FULL
657 && ospf6->gr_info.restart_in_progress) {
658 if (ospf6_gr_check_adjs(ospf6)) {
659 ospf6_gr_restart_exit(
660 ospf6, "all adjacencies were reestablished");
661 } else {
662 if (IS_DEBUG_OSPF6_GR)
663 zlog_debug(
664 "GR: not all adjacencies were reestablished yet");
665 }
666 }
667
668 return 0;
669}
670
671int config_write_ospf6_gr(struct vty *vty, struct ospf6 *ospf6)
672{
673 if (!ospf6->gr_info.restart_support)
674 return 0;
675
676 if (ospf6->gr_info.grace_period == OSPF6_DFLT_GRACE_INTERVAL)
677 vty_out(vty, " graceful-restart\n");
678 else
679 vty_out(vty, " graceful-restart grace-period %u\n",
680 ospf6->gr_info.grace_period);
681
682 return 0;
683}
684
685DEFPY(ospf6_graceful_restart_prepare, ospf6_graceful_restart_prepare_cmd,
686 "graceful-restart prepare ipv6 ospf",
687 "Graceful Restart commands\n"
688 "Prepare upcoming graceful restart\n" IPV6_STR
4a3c92de 689 "Prepare to restart the OSPFv3 process\n")
71165098
RW
690{
691 ospf6_gr_prepare();
692
693 return CMD_SUCCESS;
694}
695
696DEFPY(ospf6_graceful_restart, ospf6_graceful_restart_cmd,
697 "graceful-restart [grace-period (1-1800)$grace_period]",
698 OSPF_GR_STR
699 "Maximum length of the 'grace period'\n"
700 "Maximum length of the 'grace period' in seconds\n")
701{
702 VTY_DECLVAR_INSTANCE_CONTEXT(ospf6, ospf6);
703
704 /* Check and get restart period if present. */
705 if (!grace_period_str)
706 grace_period = OSPF6_DFLT_GRACE_INTERVAL;
707
708 ospf6->gr_info.restart_support = true;
709 ospf6->gr_info.grace_period = grace_period;
710
711 return CMD_SUCCESS;
712}
713
714DEFPY(ospf6_no_graceful_restart, ospf6_no_graceful_restart_cmd,
715 "no graceful-restart [period (1-1800)]",
716 NO_STR OSPF_GR_STR
717 "Maximum length of the 'grace period'\n"
718 "Maximum length of the 'grace period' in seconds\n")
719{
720 VTY_DECLVAR_INSTANCE_CONTEXT(ospf6, ospf6);
721
722 if (!ospf6->gr_info.restart_support)
723 return CMD_SUCCESS;
724
725 if (ospf6->gr_info.prepare_in_progress) {
726 vty_out(vty,
727 "%% Error: Graceful Restart preparation in progress\n");
728 return CMD_WARNING;
729 }
730
731 ospf6->gr_info.restart_support = false;
732 ospf6->gr_info.grace_period = OSPF6_DFLT_GRACE_INTERVAL;
733
734 return CMD_SUCCESS;
735}
736
737void ospf6_gr_init(void)
738{
739 hook_register(ospf6_neighbor_change, ospf6_gr_neighbor_change);
740
741 install_element(ENABLE_NODE, &ospf6_graceful_restart_prepare_cmd);
742 install_element(OSPF6_NODE, &ospf6_graceful_restart_cmd);
743 install_element(OSPF6_NODE, &ospf6_no_graceful_restart_cmd);
744}