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