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