diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index 8dacb9c9dc..e4da6d71e0 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -343,6 +343,10 @@ Graceful Restart To perform a graceful shutdown, the "graceful-restart prepare ipv6 ospf" EXEC-level command needs to be issued before restarting the ospf6d daemon. + When Graceful Restart is enabled and the ospf6d daemon crashes or is killed + abruptely (e.g. SIGKILL), it will attempt an unplanned Graceful Restart once + it restarts. + .. clicmd:: graceful-restart helper enable [A.B.C.D] diff --git a/ospf6d/ospf6_gr.c b/ospf6d/ospf6_gr.c index 976eb529d7..18f36abda0 100644 --- a/ospf6d/ospf6_gr.c +++ b/ospf6d/ospf6_gr.c @@ -14,6 +14,7 @@ #include "log.h" #include "hook.h" #include "printfrr.h" +#include "lib_errors.h" #include "ospf6d/ospf6_lsa.h" #include "ospf6d/ospf6_lsdb.h" @@ -25,21 +26,25 @@ #include "ospf6d/ospf6_zebra.h" #include "ospf6d/ospf6_message.h" #include "ospf6d/ospf6_neighbor.h" +#include "ospf6d/ospf6_network.h" #include "ospf6d/ospf6_flood.h" #include "ospf6d/ospf6_intra.h" #include "ospf6d/ospf6_spf.h" #include "ospf6d/ospf6_gr.h" #include "ospf6d/ospf6_gr_clippy.c" -static void ospf6_gr_nvm_delete(struct ospf6 *ospf6); +static void ospf6_gr_grace_period_expired(struct event *thread); /* Originate and install Grace-LSA for a given interface. */ -static int ospf6_gr_lsa_originate(struct ospf6_interface *oi) +static int ospf6_gr_lsa_originate(struct ospf6_interface *oi, + enum ospf6_gr_restart_reason reason) { - struct ospf6_gr_info *gr_info = &oi->area->ospf6->gr_info; + struct ospf6 *ospf6 = oi->area->ospf6; + struct ospf6_gr_info *gr_info = &ospf6->gr_info; struct ospf6_lsa_header *lsa_header; struct ospf6_grace_lsa *grace_lsa; struct ospf6_lsa *lsa; + uint16_t lsa_length; char buffer[OSPF6_MAX_LSASIZE]; if (IS_OSPF6_DEBUG_ORIGINATE(LINK)) @@ -61,29 +66,59 @@ static int ospf6_gr_lsa_originate(struct ospf6_interface *oi) /* Put restart reason. */ grace_lsa->tlv_reason.header.type = htons(RESTART_REASON_TYPE); grace_lsa->tlv_reason.header.length = htons(RESTART_REASON_LENGTH); - if (gr_info->restart_support) - grace_lsa->tlv_reason.reason = OSPF6_GR_SW_RESTART; - else - grace_lsa->tlv_reason.reason = OSPF6_GR_UNKNOWN_RESTART; + grace_lsa->tlv_reason.reason = reason; /* Fill LSA Header */ + lsa_length = sizeof(*lsa_header) + sizeof(*grace_lsa); lsa_header->age = 0; lsa_header->type = htons(OSPF6_LSTYPE_GRACE_LSA); lsa_header->id = htonl(oi->interface->ifindex); - lsa_header->adv_router = oi->area->ospf6->router_id; + lsa_header->adv_router = ospf6->router_id; lsa_header->seqnum = ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id, lsa_header->adv_router, oi->lsdb); - lsa_header->length = htons(sizeof(*lsa_header) + sizeof(*grace_lsa)); + lsa_header->length = htons(lsa_length); /* LSA checksum */ ospf6_lsa_checksum(lsa_header); - /* create LSA */ - lsa = ospf6_lsa_create(lsa_header); + if (reason == OSPF6_GR_UNKNOWN_RESTART) { + struct ospf6_header *oh; + uint32_t *uv32; + int n; + uint16_t length = OSPF6_HEADER_SIZE + 4 + lsa_length; + struct iovec iovector[2] = {}; - /* Originate */ - ospf6_lsa_originate_interface(lsa, oi); + /* Reserve space for OSPFv3 header. */ + memmove(&buffer[OSPF6_HEADER_SIZE + 4], buffer, lsa_length); + + /* Fill in the OSPFv3 header. */ + oh = (struct ospf6_header *)buffer; + oh->version = OSPFV3_VERSION; + oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; + oh->router_id = oi->area->ospf6->router_id; + oh->area_id = oi->area->area_id; + oh->instance_id = oi->instance_id; + oh->reserved = 0; + oh->length = htons(length); + + /* Fill LSA header. */ + uv32 = (uint32_t *)&buffer[sizeof(*oh)]; + *uv32 = htonl(1); + + /* Send packet. */ + iovector[0].iov_base = lsa_header; + iovector[0].iov_len = length; + n = ospf6_sendmsg(oi->linklocal_addr, &allspfrouters6, + oi->interface->ifindex, iovector, ospf6->fd); + if (n != length) + flog_err(EC_LIB_DEVELOPMENT, + "%s: could not send entire message", __func__); + } else { + /* Create and install LSA. */ + lsa = ospf6_lsa_create(lsa_header); + ospf6_lsa_originate_interface(lsa, oi); + } return 0; } @@ -134,11 +169,10 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason) ospf6->gr_info.restart_in_progress = false; ospf6->gr_info.finishing_restart = true; + XFREE(MTYPE_TMP, ospf6->gr_info.exit_reason); + ospf6->gr_info.exit_reason = XSTRDUP(MTYPE_TMP, reason); EVENT_OFF(ospf6->gr_info.t_grace_period); - /* Record in non-volatile memory that the restart is complete. */ - ospf6_gr_nvm_delete(ospf6); - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, onode, area)) { struct ospf6_interface *oi; @@ -195,6 +229,26 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason) ospf6_gr_flush_grace_lsas(ospf6); } +/* Enter the Graceful Restart mode. */ +void ospf6_gr_restart_enter(struct ospf6 *ospf6, + enum ospf6_gr_restart_reason reason, int timestamp) +{ + unsigned long remaining_time; + + ospf6->gr_info.restart_in_progress = true; + ospf6->gr_info.reason = reason; + + /* Schedule grace period timeout. */ + remaining_time = timestamp - time(NULL); + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "GR: remaining time until grace period expires: %lu(s)", + remaining_time); + + event_add_timer(master, ospf6_gr_grace_period_expired, ospf6, + remaining_time, &ospf6->gr_info.t_grace_period); +} + #define RTR_LSA_MISSING 0 #define RTR_LSA_ADJ_FOUND 1 #define RTR_LSA_ADJ_NOT_FOUND 2 @@ -470,7 +524,7 @@ static void ospf6_gr_grace_period_expired(struct event *thread) * Record in non-volatile memory that the given OSPF instance is attempting to * perform a graceful restart. */ -static void ospf6_gr_nvm_update(struct ospf6 *ospf6) +static void ospf6_gr_nvm_update(struct ospf6 *ospf6, bool prepare) { const char *inst_name; json_object *json; @@ -496,16 +550,18 @@ static void ospf6_gr_nvm_update(struct ospf6 *ospf6) json_instance); } + json_object_int_add(json_instance, "gracePeriod", + ospf6->gr_info.grace_period); + /* * Record not only the grace period, but also a UNIX timestamp * corresponding to the end of that period. That way, once ospf6d is * restarted, it will be possible to take into account the time that * passed while ospf6d wasn't running. */ - json_object_int_add(json_instance, "gracePeriod", - ospf6->gr_info.grace_period); - json_object_int_add(json_instance, "timestamp", - time(NULL) + ospf6->gr_info.grace_period); + if (prepare) + json_object_int_add(json_instance, "timestamp", + time(NULL) + ospf6->gr_info.grace_period); json_object_to_file_ext((char *)OSPF6D_GR_STATE, json, JSON_C_TO_STRING_PRETTY); @@ -516,7 +572,7 @@ static void ospf6_gr_nvm_update(struct ospf6 *ospf6) * Delete GR status information about the given OSPF instance from non-volatile * memory. */ -static void ospf6_gr_nvm_delete(struct ospf6 *ospf6) +void ospf6_gr_nvm_delete(struct ospf6 *ospf6) { const char *inst_name; json_object *json; @@ -552,6 +608,7 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6) json_object *json_instances; json_object *json_instance; json_object *json_timestamp; + json_object *json_grace_period; time_t timestamp = 0; inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; @@ -573,29 +630,33 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6) json_instance); } + json_object_object_get_ex(json_instance, "gracePeriod", + &json_grace_period); json_object_object_get_ex(json_instance, "timestamp", &json_timestamp); if (json_timestamp) { time_t now; - unsigned long remaining_time; - /* Check if the grace period has already expired. */ + /* Planned GR: check if the grace period has already expired. */ now = time(NULL); timestamp = json_object_get_int(json_timestamp); if (now > timestamp) { ospf6_gr_restart_exit( ospf6, "grace period has expired already"); - } else { - /* Schedule grace period timeout. */ - ospf6->gr_info.restart_in_progress = true; - remaining_time = timestamp - time(NULL); - if (IS_DEBUG_OSPF6_GR) - zlog_debug( - "GR: remaining time until grace period expires: %lu(s)", - remaining_time); - event_add_timer(master, ospf6_gr_grace_period_expired, - ospf6, remaining_time, - &ospf6->gr_info.t_grace_period); - } + } else + ospf6_gr_restart_enter(ospf6, OSPF6_GR_SW_RESTART, + timestamp); + } else if (json_grace_period) { + uint32_t grace_period; + + /* + * Unplanned GR: the Grace-LSAs will be sent later as soon as + * the interfaces are operational. + */ + grace_period = json_object_get_int(json_grace_period); + ospf6->gr_info.grace_period = grace_period; + ospf6_gr_restart_enter(ospf6, OSPF6_GR_UNKNOWN_RESTART, + time(NULL) + + ospf6->gr_info.grace_period); } json_object_object_del(json_instances, inst_name); @@ -605,6 +666,19 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6) json_object_free(json); } +void ospf6_gr_unplanned_start_interface(struct ospf6_interface *oi) +{ + /* + * Can't check OSPF interface state as the OSPF instance might not be + * enabled yet. + */ + if (!if_is_operative(oi->interface) || if_is_loopback(oi->interface)) + return; + + /* Send Grace-LSA. */ + ospf6_gr_lsa_originate(oi, oi->area->ospf6->gr_info.reason); +} + /* Prepare to start a Graceful Restart. */ static void ospf6_gr_prepare(void) { @@ -625,25 +699,17 @@ static void ospf6_gr_prepare(void) ospf6->gr_info.grace_period, ospf6_vrf_id_to_name(ospf6->vrf_id)); - /* Freeze OSPF routes in the RIB. */ - if (ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period)) { - zlog_warn( - "%s: failed to activate graceful restart: not connected to zebra", - __func__); - continue; - } - /* Send a Grace-LSA to all neighbors. */ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, area)) { for (ALL_LIST_ELEMENTS_RO(area->if_list, inode, oi)) { if (oi->state < OSPF6_INTERFACE_POINTTOPOINT) continue; - ospf6_gr_lsa_originate(oi); + ospf6_gr_lsa_originate(oi, OSPF6_GR_SW_RESTART); } } /* Record end of the grace period in non-volatile memory. */ - ospf6_gr_nvm_update(ospf6); + ospf6_gr_nvm_update(ospf6, true); /* * Mark that a Graceful Restart preparation is in progress, to @@ -714,6 +780,12 @@ DEFPY(ospf6_graceful_restart, ospf6_graceful_restart_cmd, ospf6->gr_info.restart_support = true; ospf6->gr_info.grace_period = grace_period; + /* Freeze OSPF routes in the RIB. */ + (void)ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period); + + /* Record that GR is enabled in non-volatile memory. */ + ospf6_gr_nvm_update(ospf6, false); + return CMD_SUCCESS; } @@ -736,6 +808,8 @@ DEFPY(ospf6_no_graceful_restart, ospf6_no_graceful_restart_cmd, ospf6->gr_info.restart_support = false; ospf6->gr_info.grace_period = OSPF6_DFLT_GRACE_INTERVAL; + ospf6_gr_nvm_delete(ospf6); + ospf6_zebra_gr_disable(ospf6); return CMD_SUCCESS; } diff --git a/ospf6d/ospf6_gr.h b/ospf6d/ospf6_gr.h index 2c7e8b341c..42c7bab61b 100644 --- a/ospf6d/ospf6_gr.h +++ b/ospf6d/ospf6_gr.h @@ -155,9 +155,14 @@ extern int config_write_ospf6_gr(struct vty *vty, struct ospf6 *ospf6); extern int config_write_ospf6_gr_helper(struct vty *vty, struct ospf6 *ospf6); extern int config_write_ospf6_debug_gr_helper(struct vty *vty); +extern void ospf6_gr_restart_enter(struct ospf6 *ospf6, + enum ospf6_gr_restart_reason reason, + int timestamp); extern void ospf6_gr_check_lsdb_consistency(struct ospf6 *ospf, struct ospf6_area *area); extern void ospf6_gr_nvm_read(struct ospf6 *ospf); +extern void ospf6_gr_nvm_delete(struct ospf6 *ospf6); +extern void ospf6_gr_unplanned_start_interface(struct ospf6_interface *oi); extern void ospf6_gr_init(void); #endif /* OSPF6_GR_H */ diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index e7148d66ba..0f6d9e10d2 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -772,6 +772,17 @@ void interface_up(struct event *thread) return; } + /* + * RFC 3623 - Section 5 ("Unplanned Outages"): + * "The grace-LSAs are encapsulated in Link State Update Packets + * and sent out to all interfaces, even though the restarted + * router has no adjacencies and no knowledge of previous + * adjacencies". + */ + if (oi->area->ospf6->gr_info.restart_in_progress && + oi->area->ospf6->gr_info.reason == OSPF6_GR_UNKNOWN_RESTART) + ospf6_gr_unplanned_start_interface(oi); + #ifdef __FreeBSD__ /* * There's a delay in FreeBSD between issuing a command to leave a diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index 0990b14307..e39ae504a2 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -1262,6 +1262,7 @@ static void ospf6_ase_calculate_timer(struct event *t) * no longer valid. */ ospf6_zebra_gr_disable(ospf6); + ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period); ospf6->gr_info.finishing_restart = false; } } diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index c2aa3abeed..01c962194c 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -455,6 +455,13 @@ struct ospf6 *ospf6_instance_create(const char *name) if (ospf6->router_id == 0) ospf6_router_id_update(ospf6, true); ospf6_add(ospf6); + + /* + * Read from non-volatile memory whether this instance is performing a + * graceful restart or not. + */ + ospf6_gr_nvm_read(ospf6); + if (ospf6->vrf_id != VRF_UNKNOWN) { vrf = vrf_lookup_by_id(ospf6->vrf_id); FOR_ALL_INTERFACES (vrf, ifp) { @@ -465,12 +472,6 @@ struct ospf6 *ospf6_instance_create(const char *name) if (ospf6->fd < 0) return ospf6; - /* - * Read from non-volatile memory whether this instance is performing a - * graceful restart or not. - */ - ospf6_gr_nvm_read(ospf6); - event_add_read(master, ospf6_receive, ospf6, ospf6->fd, &ospf6->t_ospf6_receive); @@ -490,6 +491,7 @@ void ospf6_delete(struct ospf6 *o) ospf6_gr_helper_deinit(o); if (!o->gr_info.prepare_in_progress) ospf6_flush_self_originated_lsas_now(o); + XFREE(MTYPE_TMP, o->gr_info.exit_reason); ospf6_disable(o); ospf6_del(o); @@ -693,6 +695,9 @@ DEFUN(no_router_ospf6, no_router_ospf6_cmd, "no router ospf6 [vrf NAME]", if (ospf6 == NULL) vty_out(vty, "OSPFv3 is not configured\n"); else { + if (ospf6->gr_info.restart_support) + ospf6_gr_nvm_delete(ospf6); + ospf6_delete(ospf6); ospf6 = NULL; } diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index 8fdd291122..a38dad8fce 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -51,6 +51,8 @@ struct ospf6_gr_info { bool prepare_in_progress; bool finishing_restart; uint32_t grace_period; + int reason; + char *exit_reason; struct event *t_grace_period; }; diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index 6b3d4955da..1132c0a801 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -735,10 +735,20 @@ uint8_t ospf6_distance_apply(struct prefix_ipv6 *p, struct ospf6_route * or, static void ospf6_zebra_connected(struct zclient *zclient) { + struct ospf6 *ospf6; + struct listnode *node; + /* Send the client registration */ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT); zclient_send_reg_requests(zclient, VRF_DEFAULT); + + /* Activate graceful restart if configured. */ + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (!ospf6->gr_info.restart_support) + continue; + (void)ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period); + } } static zclient_handler *const ospf6_handlers[] = {