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