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