mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-04-29 10:32:00 +00:00
Merge pull request #12949 from opensourcerouting/ospf-unplanned-gr
OSPF GR for unplanned outages
This commit is contained in:
commit
425fc1f5b7
@ -287,6 +287,19 @@ OSPF6 interface
|
|||||||
|
|
||||||
Sets interface's Router Dead Interval. Default value is 40.
|
Sets interface's Router Dead Interval. Default value is 40.
|
||||||
|
|
||||||
|
.. clicmd:: ipv6 ospf6 graceful-restart hello-delay HELLODELAYINTERVAL
|
||||||
|
|
||||||
|
Set the length of time during which Grace-LSAs are sent at 1-second intervals
|
||||||
|
while coming back up after an unplanned outage. During this time, no hello
|
||||||
|
packets are sent.
|
||||||
|
|
||||||
|
A higher hello delay will increase the chance that all neighbors are notified
|
||||||
|
about the ongoing graceful restart before receiving a hello packet (which is
|
||||||
|
crucial for the graceful restart to succeed). The hello delay shouldn't be set
|
||||||
|
too high, however, otherwise the adjacencies might time out. As a best practice,
|
||||||
|
it's recommended to set the hello delay and hello interval with the same values.
|
||||||
|
The default value is 10 seconds.
|
||||||
|
|
||||||
.. clicmd:: ipv6 ospf6 retransmit-interval RETRANSMITINTERVAL
|
.. clicmd:: ipv6 ospf6 retransmit-interval RETRANSMITINTERVAL
|
||||||
|
|
||||||
Sets interface's Rxmt Interval. Default value is 5.
|
Sets interface's Rxmt Interval. Default value is 5.
|
||||||
@ -343,6 +356,10 @@ Graceful Restart
|
|||||||
To perform a graceful shutdown, the "graceful-restart prepare ipv6 ospf"
|
To perform a graceful shutdown, the "graceful-restart prepare ipv6 ospf"
|
||||||
EXEC-level command needs to be issued before restarting the ospf6d daemon.
|
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]
|
.. clicmd:: graceful-restart helper enable [A.B.C.D]
|
||||||
|
|
||||||
|
|
||||||
|
@ -635,6 +635,19 @@ Interfaces
|
|||||||
:clicmd:`ip ospf dead-interval minimal hello-multiplier (2-20)` is also
|
:clicmd:`ip ospf dead-interval minimal hello-multiplier (2-20)` is also
|
||||||
specified for the interface.
|
specified for the interface.
|
||||||
|
|
||||||
|
.. clicmd:: ip ospf graceful-restart hello-delay (1-1800)
|
||||||
|
|
||||||
|
Set the length of time during which Grace-LSAs are sent at 1-second intervals
|
||||||
|
while coming back up after an unplanned outage. During this time, no hello
|
||||||
|
packets are sent.
|
||||||
|
|
||||||
|
A higher hello delay will increase the chance that all neighbors are notified
|
||||||
|
about the ongoing graceful restart before receiving a hello packet (which is
|
||||||
|
crucial for the graceful restart to succeed). The hello delay shouldn't be set
|
||||||
|
too high, however, otherwise the adjacencies might time out. As a best practice,
|
||||||
|
it's recommended to set the hello delay and hello interval with the same values.
|
||||||
|
The default value is 10 seconds.
|
||||||
|
|
||||||
.. clicmd:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point [dmvpn])
|
.. clicmd:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point [dmvpn])
|
||||||
|
|
||||||
When configuring a point-to-point network on an interface and the interface
|
When configuring a point-to-point network on an interface and the interface
|
||||||
@ -770,6 +783,10 @@ Graceful Restart
|
|||||||
To perform a graceful shutdown, the "graceful-restart prepare ip ospf"
|
To perform a graceful shutdown, the "graceful-restart prepare ip ospf"
|
||||||
EXEC-level command needs to be issued before restarting the ospfd daemon.
|
EXEC-level command needs to be issued before restarting the ospfd daemon.
|
||||||
|
|
||||||
|
When Graceful Restart is enabled and the ospfd 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]
|
.. clicmd:: graceful-restart helper enable [A.B.C.D]
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,6 +55,7 @@ extern "C" {
|
|||||||
#define OSPF_ROUTER_DEAD_INTERVAL_DEFAULT 40
|
#define OSPF_ROUTER_DEAD_INTERVAL_DEFAULT 40
|
||||||
#define OSPF_ROUTER_DEAD_INTERVAL_MINIMAL 1
|
#define OSPF_ROUTER_DEAD_INTERVAL_MINIMAL 1
|
||||||
#define OSPF_HELLO_INTERVAL_DEFAULT 10
|
#define OSPF_HELLO_INTERVAL_DEFAULT 10
|
||||||
|
#define OSPF_HELLO_DELAY_DEFAULT 10
|
||||||
#define OSPF_ROUTER_PRIORITY_DEFAULT 1
|
#define OSPF_ROUTER_PRIORITY_DEFAULT 1
|
||||||
#define OSPF_RETRANSMIT_INTERVAL_DEFAULT 5
|
#define OSPF_RETRANSMIT_INTERVAL_DEFAULT 5
|
||||||
#define OSPF_TRANSMIT_DELAY_DEFAULT 1
|
#define OSPF_TRANSMIT_DELAY_DEFAULT 1
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "hook.h"
|
#include "hook.h"
|
||||||
#include "printfrr.h"
|
#include "printfrr.h"
|
||||||
|
#include "lib_errors.h"
|
||||||
|
|
||||||
#include "ospf6d/ospf6_lsa.h"
|
#include "ospf6d/ospf6_lsa.h"
|
||||||
#include "ospf6d/ospf6_lsdb.h"
|
#include "ospf6d/ospf6_lsdb.h"
|
||||||
@ -25,21 +26,25 @@
|
|||||||
#include "ospf6d/ospf6_zebra.h"
|
#include "ospf6d/ospf6_zebra.h"
|
||||||
#include "ospf6d/ospf6_message.h"
|
#include "ospf6d/ospf6_message.h"
|
||||||
#include "ospf6d/ospf6_neighbor.h"
|
#include "ospf6d/ospf6_neighbor.h"
|
||||||
|
#include "ospf6d/ospf6_network.h"
|
||||||
#include "ospf6d/ospf6_flood.h"
|
#include "ospf6d/ospf6_flood.h"
|
||||||
#include "ospf6d/ospf6_intra.h"
|
#include "ospf6d/ospf6_intra.h"
|
||||||
#include "ospf6d/ospf6_spf.h"
|
#include "ospf6d/ospf6_spf.h"
|
||||||
#include "ospf6d/ospf6_gr.h"
|
#include "ospf6d/ospf6_gr.h"
|
||||||
#include "ospf6d/ospf6_gr_clippy.c"
|
#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. */
|
/* 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_lsa_header *lsa_header;
|
||||||
struct ospf6_grace_lsa *grace_lsa;
|
struct ospf6_grace_lsa *grace_lsa;
|
||||||
struct ospf6_lsa *lsa;
|
struct ospf6_lsa *lsa;
|
||||||
|
uint16_t lsa_length;
|
||||||
char buffer[OSPF6_MAX_LSASIZE];
|
char buffer[OSPF6_MAX_LSASIZE];
|
||||||
|
|
||||||
if (IS_OSPF6_DEBUG_ORIGINATE(LINK))
|
if (IS_OSPF6_DEBUG_ORIGINATE(LINK))
|
||||||
@ -61,29 +66,59 @@ static int ospf6_gr_lsa_originate(struct ospf6_interface *oi)
|
|||||||
/* Put restart reason. */
|
/* Put restart reason. */
|
||||||
grace_lsa->tlv_reason.header.type = htons(RESTART_REASON_TYPE);
|
grace_lsa->tlv_reason.header.type = htons(RESTART_REASON_TYPE);
|
||||||
grace_lsa->tlv_reason.header.length = htons(RESTART_REASON_LENGTH);
|
grace_lsa->tlv_reason.header.length = htons(RESTART_REASON_LENGTH);
|
||||||
if (gr_info->restart_support)
|
grace_lsa->tlv_reason.reason = reason;
|
||||||
grace_lsa->tlv_reason.reason = OSPF6_GR_SW_RESTART;
|
|
||||||
else
|
|
||||||
grace_lsa->tlv_reason.reason = OSPF6_GR_UNKNOWN_RESTART;
|
|
||||||
|
|
||||||
/* Fill LSA Header */
|
/* Fill LSA Header */
|
||||||
|
lsa_length = sizeof(*lsa_header) + sizeof(*grace_lsa);
|
||||||
lsa_header->age = 0;
|
lsa_header->age = 0;
|
||||||
lsa_header->type = htons(OSPF6_LSTYPE_GRACE_LSA);
|
lsa_header->type = htons(OSPF6_LSTYPE_GRACE_LSA);
|
||||||
lsa_header->id = htonl(oi->interface->ifindex);
|
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 =
|
lsa_header->seqnum =
|
||||||
ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id,
|
ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id,
|
||||||
lsa_header->adv_router, oi->lsdb);
|
lsa_header->adv_router, oi->lsdb);
|
||||||
lsa_header->length = htons(sizeof(*lsa_header) + sizeof(*grace_lsa));
|
lsa_header->length = htons(lsa_length);
|
||||||
|
|
||||||
/* LSA checksum */
|
/* LSA checksum */
|
||||||
ospf6_lsa_checksum(lsa_header);
|
ospf6_lsa_checksum(lsa_header);
|
||||||
|
|
||||||
/* create LSA */
|
if (reason == OSPF6_GR_UNKNOWN_RESTART) {
|
||||||
lsa = ospf6_lsa_create(lsa_header);
|
struct ospf6_header *oh;
|
||||||
|
uint32_t *uv32;
|
||||||
|
int n;
|
||||||
|
uint16_t length = OSPF6_HEADER_SIZE + 4 + lsa_length;
|
||||||
|
struct iovec iovector[2] = {};
|
||||||
|
|
||||||
/* Originate */
|
/* 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);
|
ospf6_lsa_originate_interface(lsa, oi);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
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.restart_in_progress = false;
|
||||||
ospf6->gr_info.finishing_restart = true;
|
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);
|
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)) {
|
for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, onode, area)) {
|
||||||
struct ospf6_interface *oi;
|
struct ospf6_interface *oi;
|
||||||
|
|
||||||
@ -156,6 +190,14 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason)
|
|||||||
OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(area);
|
OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(area);
|
||||||
|
|
||||||
for (ALL_LIST_ELEMENTS_RO(area->if_list, anode, oi)) {
|
for (ALL_LIST_ELEMENTS_RO(area->if_list, anode, oi)) {
|
||||||
|
/* Disable hello delay. */
|
||||||
|
if (oi->gr.hello_delay.t_grace_send) {
|
||||||
|
oi->gr.hello_delay.elapsed_seconds = 0;
|
||||||
|
EVENT_OFF(oi->gr.hello_delay.t_grace_send);
|
||||||
|
event_add_event(master, ospf6_hello_send, oi, 0,
|
||||||
|
&oi->thread_send_hello);
|
||||||
|
}
|
||||||
|
|
||||||
/* Reoriginate Link-LSA. */
|
/* Reoriginate Link-LSA. */
|
||||||
if (oi->type != OSPF_IFTYPE_VIRTUALLINK)
|
if (oi->type != OSPF_IFTYPE_VIRTUALLINK)
|
||||||
OSPF6_LINK_LSA_EXECUTE(oi);
|
OSPF6_LINK_LSA_EXECUTE(oi);
|
||||||
@ -195,6 +237,26 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason)
|
|||||||
ospf6_gr_flush_grace_lsas(ospf6);
|
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_MISSING 0
|
||||||
#define RTR_LSA_ADJ_FOUND 1
|
#define RTR_LSA_ADJ_FOUND 1
|
||||||
#define RTR_LSA_ADJ_NOT_FOUND 2
|
#define RTR_LSA_ADJ_NOT_FOUND 2
|
||||||
@ -466,11 +528,26 @@ static void ospf6_gr_grace_period_expired(struct event *thread)
|
|||||||
ospf6_gr_restart_exit(ospf6, "grace period has expired");
|
ospf6_gr_restart_exit(ospf6, "grace period has expired");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Send extra Grace-LSA out the interface (unplanned outages only). */
|
||||||
|
void ospf6_gr_iface_send_grace_lsa(struct event *thread)
|
||||||
|
{
|
||||||
|
struct ospf6_interface *oi = EVENT_ARG(thread);
|
||||||
|
|
||||||
|
ospf6_gr_lsa_originate(oi, oi->area->ospf6->gr_info.reason);
|
||||||
|
|
||||||
|
if (++oi->gr.hello_delay.elapsed_seconds < oi->gr.hello_delay.interval)
|
||||||
|
event_add_timer(master, ospf6_gr_iface_send_grace_lsa, oi, 1,
|
||||||
|
&oi->gr.hello_delay.t_grace_send);
|
||||||
|
else
|
||||||
|
event_add_event(master, ospf6_hello_send, oi, 0,
|
||||||
|
&oi->thread_send_hello);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Record in non-volatile memory that the given OSPF instance is attempting to
|
* Record in non-volatile memory that the given OSPF instance is attempting to
|
||||||
* perform a graceful restart.
|
* 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;
|
const char *inst_name;
|
||||||
json_object *json;
|
json_object *json;
|
||||||
@ -496,14 +573,16 @@ static void ospf6_gr_nvm_update(struct ospf6 *ospf6)
|
|||||||
json_instance);
|
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
|
* Record not only the grace period, but also a UNIX timestamp
|
||||||
* corresponding to the end of that period. That way, once ospf6d is
|
* corresponding to the end of that period. That way, once ospf6d is
|
||||||
* restarted, it will be possible to take into account the time that
|
* restarted, it will be possible to take into account the time that
|
||||||
* passed while ospf6d wasn't running.
|
* passed while ospf6d wasn't running.
|
||||||
*/
|
*/
|
||||||
json_object_int_add(json_instance, "gracePeriod",
|
if (prepare)
|
||||||
ospf6->gr_info.grace_period);
|
|
||||||
json_object_int_add(json_instance, "timestamp",
|
json_object_int_add(json_instance, "timestamp",
|
||||||
time(NULL) + ospf6->gr_info.grace_period);
|
time(NULL) + ospf6->gr_info.grace_period);
|
||||||
|
|
||||||
@ -516,7 +595,7 @@ static void ospf6_gr_nvm_update(struct ospf6 *ospf6)
|
|||||||
* Delete GR status information about the given OSPF instance from non-volatile
|
* Delete GR status information about the given OSPF instance from non-volatile
|
||||||
* memory.
|
* memory.
|
||||||
*/
|
*/
|
||||||
static void ospf6_gr_nvm_delete(struct ospf6 *ospf6)
|
void ospf6_gr_nvm_delete(struct ospf6 *ospf6)
|
||||||
{
|
{
|
||||||
const char *inst_name;
|
const char *inst_name;
|
||||||
json_object *json;
|
json_object *json;
|
||||||
@ -552,6 +631,7 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6)
|
|||||||
json_object *json_instances;
|
json_object *json_instances;
|
||||||
json_object *json_instance;
|
json_object *json_instance;
|
||||||
json_object *json_timestamp;
|
json_object *json_timestamp;
|
||||||
|
json_object *json_grace_period;
|
||||||
time_t timestamp = 0;
|
time_t timestamp = 0;
|
||||||
|
|
||||||
inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME;
|
inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME;
|
||||||
@ -573,29 +653,33 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6)
|
|||||||
json_instance);
|
json_instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
json_object_object_get_ex(json_instance, "gracePeriod",
|
||||||
|
&json_grace_period);
|
||||||
json_object_object_get_ex(json_instance, "timestamp", &json_timestamp);
|
json_object_object_get_ex(json_instance, "timestamp", &json_timestamp);
|
||||||
if (json_timestamp) {
|
if (json_timestamp) {
|
||||||
time_t now;
|
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);
|
now = time(NULL);
|
||||||
timestamp = json_object_get_int(json_timestamp);
|
timestamp = json_object_get_int(json_timestamp);
|
||||||
if (now > timestamp) {
|
if (now > timestamp) {
|
||||||
ospf6_gr_restart_exit(
|
ospf6_gr_restart_exit(
|
||||||
ospf6, "grace period has expired already");
|
ospf6, "grace period has expired already");
|
||||||
} else {
|
} else
|
||||||
/* Schedule grace period timeout. */
|
ospf6_gr_restart_enter(ospf6, OSPF6_GR_SW_RESTART,
|
||||||
ospf6->gr_info.restart_in_progress = true;
|
timestamp);
|
||||||
remaining_time = timestamp - time(NULL);
|
} else if (json_grace_period) {
|
||||||
if (IS_DEBUG_OSPF6_GR)
|
uint32_t grace_period;
|
||||||
zlog_debug(
|
|
||||||
"GR: remaining time until grace period expires: %lu(s)",
|
/*
|
||||||
remaining_time);
|
* Unplanned GR: the Grace-LSAs will be sent later as soon as
|
||||||
event_add_timer(master, ospf6_gr_grace_period_expired,
|
* the interfaces are operational.
|
||||||
ospf6, remaining_time,
|
*/
|
||||||
&ospf6->gr_info.t_grace_period);
|
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);
|
json_object_object_del(json_instances, inst_name);
|
||||||
@ -605,6 +689,24 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6)
|
|||||||
json_object_free(json);
|
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);
|
||||||
|
|
||||||
|
/* Start GR hello-delay interval. */
|
||||||
|
oi->gr.hello_delay.elapsed_seconds = 0;
|
||||||
|
event_add_timer(master, ospf6_gr_iface_send_grace_lsa, oi, 1,
|
||||||
|
&oi->gr.hello_delay.t_grace_send);
|
||||||
|
}
|
||||||
|
|
||||||
/* Prepare to start a Graceful Restart. */
|
/* Prepare to start a Graceful Restart. */
|
||||||
static void ospf6_gr_prepare(void)
|
static void ospf6_gr_prepare(void)
|
||||||
{
|
{
|
||||||
@ -625,25 +727,17 @@ static void ospf6_gr_prepare(void)
|
|||||||
ospf6->gr_info.grace_period,
|
ospf6->gr_info.grace_period,
|
||||||
ospf6_vrf_id_to_name(ospf6->vrf_id));
|
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. */
|
/* Send a Grace-LSA to all neighbors. */
|
||||||
for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, area)) {
|
for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, area)) {
|
||||||
for (ALL_LIST_ELEMENTS_RO(area->if_list, inode, oi)) {
|
for (ALL_LIST_ELEMENTS_RO(area->if_list, inode, oi)) {
|
||||||
if (oi->state < OSPF6_INTERFACE_POINTTOPOINT)
|
if (oi->state < OSPF6_INTERFACE_POINTTOPOINT)
|
||||||
continue;
|
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. */
|
/* 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
|
* Mark that a Graceful Restart preparation is in progress, to
|
||||||
@ -714,6 +808,12 @@ DEFPY(ospf6_graceful_restart, ospf6_graceful_restart_cmd,
|
|||||||
ospf6->gr_info.restart_support = true;
|
ospf6->gr_info.restart_support = true;
|
||||||
ospf6->gr_info.grace_period = grace_period;
|
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;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -736,6 +836,8 @@ DEFPY(ospf6_no_graceful_restart, ospf6_no_graceful_restart_cmd,
|
|||||||
|
|
||||||
ospf6->gr_info.restart_support = false;
|
ospf6->gr_info.restart_support = false;
|
||||||
ospf6->gr_info.grace_period = OSPF6_DFLT_GRACE_INTERVAL;
|
ospf6->gr_info.grace_period = OSPF6_DFLT_GRACE_INTERVAL;
|
||||||
|
ospf6_gr_nvm_delete(ospf6);
|
||||||
|
ospf6_zebra_gr_disable(ospf6);
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -155,9 +155,15 @@ 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_gr_helper(struct vty *vty, struct ospf6 *ospf6);
|
||||||
extern int config_write_ospf6_debug_gr_helper(struct vty *vty);
|
extern int config_write_ospf6_debug_gr_helper(struct vty *vty);
|
||||||
|
|
||||||
|
extern void ospf6_gr_iface_send_grace_lsa(struct event *thread);
|
||||||
|
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,
|
extern void ospf6_gr_check_lsdb_consistency(struct ospf6 *ospf,
|
||||||
struct ospf6_area *area);
|
struct ospf6_area *area);
|
||||||
extern void ospf6_gr_nvm_read(struct ospf6 *ospf);
|
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);
|
extern void ospf6_gr_init(void);
|
||||||
|
|
||||||
#endif /* OSPF6_GR_H */
|
#endif /* OSPF6_GR_H */
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#include "ospf6_proto.h"
|
#include "ospf6_proto.h"
|
||||||
#include "lib/keychain.h"
|
#include "lib/keychain.h"
|
||||||
#include "ospf6_auth_trailer.h"
|
#include "ospf6_auth_trailer.h"
|
||||||
|
#include "ospf6d/ospf6_interface_clippy.c"
|
||||||
|
|
||||||
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_IF, "OSPF6 interface");
|
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_IF, "OSPF6 interface");
|
||||||
DEFINE_MTYPE(OSPF6D, OSPF6_AUTH_KEYCHAIN, "OSPF6 auth keychain");
|
DEFINE_MTYPE(OSPF6D, OSPF6_AUTH_KEYCHAIN, "OSPF6 auth keychain");
|
||||||
@ -202,6 +203,7 @@ struct ospf6_interface *ospf6_interface_create(struct interface *ifp)
|
|||||||
oi->priority = OSPF6_INTERFACE_PRIORITY;
|
oi->priority = OSPF6_INTERFACE_PRIORITY;
|
||||||
|
|
||||||
oi->hello_interval = OSPF_HELLO_INTERVAL_DEFAULT;
|
oi->hello_interval = OSPF_HELLO_INTERVAL_DEFAULT;
|
||||||
|
oi->gr.hello_delay.interval = OSPF_HELLO_DELAY_DEFAULT;
|
||||||
oi->dead_interval = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT;
|
oi->dead_interval = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT;
|
||||||
oi->rxmt_interval = OSPF_RETRANSMIT_INTERVAL_DEFAULT;
|
oi->rxmt_interval = OSPF_RETRANSMIT_INTERVAL_DEFAULT;
|
||||||
oi->type = ospf6_default_iftype(ifp);
|
oi->type = ospf6_default_iftype(ifp);
|
||||||
@ -324,6 +326,9 @@ void ospf6_interface_disable(struct ospf6_interface *oi)
|
|||||||
EVENT_OFF(oi->thread_intra_prefix_lsa);
|
EVENT_OFF(oi->thread_intra_prefix_lsa);
|
||||||
EVENT_OFF(oi->thread_as_extern_lsa);
|
EVENT_OFF(oi->thread_as_extern_lsa);
|
||||||
EVENT_OFF(oi->thread_wait_timer);
|
EVENT_OFF(oi->thread_wait_timer);
|
||||||
|
|
||||||
|
oi->gr.hello_delay.elapsed_seconds = 0;
|
||||||
|
EVENT_OFF(oi->gr.hello_delay.t_grace_send);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct in6_addr *
|
static struct in6_addr *
|
||||||
@ -772,6 +777,17 @@ void interface_up(struct event *thread)
|
|||||||
return;
|
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__
|
#ifdef __FreeBSD__
|
||||||
/*
|
/*
|
||||||
* There's a delay in FreeBSD between issuing a command to leave a
|
* There's a delay in FreeBSD between issuing a command to leave a
|
||||||
@ -1180,6 +1196,9 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp,
|
|||||||
json_arr, json_object_new_string(lsa->name));
|
json_arr, json_object_new_string(lsa->name));
|
||||||
json_object_object_add(json_obj, "pendingLsaLsAck", json_arr);
|
json_object_object_add(json_obj, "pendingLsaLsAck", json_arr);
|
||||||
|
|
||||||
|
if (oi->gr.hello_delay.interval != 0)
|
||||||
|
json_object_int_add(json_obj, "grHelloDelaySecs",
|
||||||
|
oi->gr.hello_delay.interval);
|
||||||
} else {
|
} else {
|
||||||
timerclear(&res);
|
timerclear(&res);
|
||||||
if (event_is_scheduled(oi->thread_send_lsupdate))
|
if (event_is_scheduled(oi->thread_send_lsupdate))
|
||||||
@ -1205,6 +1224,10 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp,
|
|||||||
: "off"));
|
: "off"));
|
||||||
for (ALL_LSDB(oi->lsack_list, lsa, lsanext))
|
for (ALL_LSDB(oi->lsack_list, lsa, lsanext))
|
||||||
vty_out(vty, " %s\n", lsa->name);
|
vty_out(vty, " %s\n", lsa->name);
|
||||||
|
|
||||||
|
if (oi->gr.hello_delay.interval != 0)
|
||||||
|
vty_out(vty, " Graceful Restart hello delay: %us\n",
|
||||||
|
oi->gr.hello_delay.interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* BFD specific. */
|
/* BFD specific. */
|
||||||
@ -2157,6 +2180,50 @@ ALIAS (ipv6_ospf6_deadinterval,
|
|||||||
"Interval time after which a neighbor is declared down\n"
|
"Interval time after which a neighbor is declared down\n"
|
||||||
SECONDS_STR)
|
SECONDS_STR)
|
||||||
|
|
||||||
|
DEFPY(ipv6_ospf6_gr_hdelay, ipv6_ospf6_gr_hdelay_cmd,
|
||||||
|
"ipv6 ospf6 graceful-restart hello-delay (1-1800)",
|
||||||
|
IP6_STR
|
||||||
|
OSPF6_STR
|
||||||
|
"Graceful Restart parameters\n"
|
||||||
|
"Delay the sending of the first hello packets.\n"
|
||||||
|
"Delay in seconds\n")
|
||||||
|
{
|
||||||
|
VTY_DECLVAR_CONTEXT(interface, ifp);
|
||||||
|
struct ospf6_interface *oi;
|
||||||
|
|
||||||
|
oi = ifp->info;
|
||||||
|
if (oi == NULL)
|
||||||
|
oi = ospf6_interface_create(ifp);
|
||||||
|
|
||||||
|
/* Note: new or updated value won't affect ongoing graceful restart. */
|
||||||
|
oi->gr.hello_delay.interval = hello_delay;
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFPY(no_ipv6_ospf6_gr_hdelay, no_ipv6_ospf6_gr_hdelay_cmd,
|
||||||
|
"no ipv6 ospf6 graceful-restart hello-delay [(1-1800)]",
|
||||||
|
NO_STR
|
||||||
|
IP6_STR
|
||||||
|
OSPF6_STR
|
||||||
|
"Graceful Restart parameters\n"
|
||||||
|
"Delay the sending of the first hello packets.\n"
|
||||||
|
"Delay in seconds\n")
|
||||||
|
{
|
||||||
|
VTY_DECLVAR_CONTEXT(interface, ifp);
|
||||||
|
struct ospf6_interface *oi;
|
||||||
|
|
||||||
|
oi = ifp->info;
|
||||||
|
if (oi == NULL)
|
||||||
|
oi = ospf6_interface_create(ifp);
|
||||||
|
|
||||||
|
oi->gr.hello_delay.interval = OSPF_HELLO_DELAY_DEFAULT;
|
||||||
|
oi->gr.hello_delay.elapsed_seconds = 0;
|
||||||
|
EVENT_OFF(oi->gr.hello_delay.t_grace_send);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
/* interface variable set command */
|
/* interface variable set command */
|
||||||
DEFUN (ipv6_ospf6_transmitdelay,
|
DEFUN (ipv6_ospf6_transmitdelay,
|
||||||
ipv6_ospf6_transmitdelay_cmd,
|
ipv6_ospf6_transmitdelay_cmd,
|
||||||
@ -2624,6 +2691,11 @@ static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf)
|
|||||||
else if (oi->type_cfg && oi->type == OSPF_IFTYPE_BROADCAST)
|
else if (oi->type_cfg && oi->type == OSPF_IFTYPE_BROADCAST)
|
||||||
vty_out(vty, " ipv6 ospf6 network broadcast\n");
|
vty_out(vty, " ipv6 ospf6 network broadcast\n");
|
||||||
|
|
||||||
|
if (oi->gr.hello_delay.interval != OSPF_HELLO_DELAY_DEFAULT)
|
||||||
|
vty_out(vty,
|
||||||
|
" ipv6 ospf6 graceful-restart hello-delay %u\n",
|
||||||
|
oi->gr.hello_delay.interval);
|
||||||
|
|
||||||
ospf6_bfd_write_config(vty, oi);
|
ospf6_bfd_write_config(vty, oi);
|
||||||
|
|
||||||
ospf6_auth_write_config(vty, &oi->at_data);
|
ospf6_auth_write_config(vty, &oi->at_data);
|
||||||
@ -2722,12 +2794,14 @@ void ospf6_interface_init(void)
|
|||||||
|
|
||||||
install_element(INTERFACE_NODE, &ipv6_ospf6_deadinterval_cmd);
|
install_element(INTERFACE_NODE, &ipv6_ospf6_deadinterval_cmd);
|
||||||
install_element(INTERFACE_NODE, &ipv6_ospf6_hellointerval_cmd);
|
install_element(INTERFACE_NODE, &ipv6_ospf6_hellointerval_cmd);
|
||||||
|
install_element(INTERFACE_NODE, &ipv6_ospf6_gr_hdelay_cmd);
|
||||||
install_element(INTERFACE_NODE, &ipv6_ospf6_priority_cmd);
|
install_element(INTERFACE_NODE, &ipv6_ospf6_priority_cmd);
|
||||||
install_element(INTERFACE_NODE, &ipv6_ospf6_retransmitinterval_cmd);
|
install_element(INTERFACE_NODE, &ipv6_ospf6_retransmitinterval_cmd);
|
||||||
install_element(INTERFACE_NODE, &ipv6_ospf6_transmitdelay_cmd);
|
install_element(INTERFACE_NODE, &ipv6_ospf6_transmitdelay_cmd);
|
||||||
install_element(INTERFACE_NODE, &ipv6_ospf6_instance_cmd);
|
install_element(INTERFACE_NODE, &ipv6_ospf6_instance_cmd);
|
||||||
install_element(INTERFACE_NODE, &no_ipv6_ospf6_deadinterval_cmd);
|
install_element(INTERFACE_NODE, &no_ipv6_ospf6_deadinterval_cmd);
|
||||||
install_element(INTERFACE_NODE, &no_ipv6_ospf6_hellointerval_cmd);
|
install_element(INTERFACE_NODE, &no_ipv6_ospf6_hellointerval_cmd);
|
||||||
|
install_element(INTERFACE_NODE, &no_ipv6_ospf6_gr_hdelay_cmd);
|
||||||
install_element(INTERFACE_NODE, &no_ipv6_ospf6_priority_cmd);
|
install_element(INTERFACE_NODE, &no_ipv6_ospf6_priority_cmd);
|
||||||
install_element(INTERFACE_NODE, &no_ipv6_ospf6_retransmitinterval_cmd);
|
install_element(INTERFACE_NODE, &no_ipv6_ospf6_retransmitinterval_cmd);
|
||||||
install_element(INTERFACE_NODE, &no_ipv6_ospf6_transmitdelay_cmd);
|
install_element(INTERFACE_NODE, &no_ipv6_ospf6_transmitdelay_cmd);
|
||||||
|
@ -74,6 +74,15 @@ struct ospf6_interface {
|
|||||||
uint16_t dead_interval;
|
uint16_t dead_interval;
|
||||||
uint32_t rxmt_interval;
|
uint32_t rxmt_interval;
|
||||||
|
|
||||||
|
/* Graceful-Restart data. */
|
||||||
|
struct {
|
||||||
|
struct {
|
||||||
|
uint16_t interval;
|
||||||
|
uint16_t elapsed_seconds;
|
||||||
|
struct event *t_grace_send;
|
||||||
|
} hello_delay;
|
||||||
|
} gr;
|
||||||
|
|
||||||
uint32_t state_change;
|
uint32_t state_change;
|
||||||
|
|
||||||
/* Cost */
|
/* Cost */
|
||||||
|
@ -2244,6 +2244,10 @@ void ospf6_hello_send(struct event *thread)
|
|||||||
|
|
||||||
oi = (struct ospf6_interface *)EVENT_ARG(thread);
|
oi = (struct ospf6_interface *)EVENT_ARG(thread);
|
||||||
|
|
||||||
|
/* Check if the GR hello-delay is active. */
|
||||||
|
if (oi->gr.hello_delay.t_grace_send)
|
||||||
|
return;
|
||||||
|
|
||||||
if (oi->state <= OSPF6_INTERFACE_DOWN) {
|
if (oi->state <= OSPF6_INTERFACE_DOWN) {
|
||||||
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO, SEND_HDR))
|
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO, SEND_HDR))
|
||||||
zlog_debug("Unable to send Hello on down interface %s",
|
zlog_debug("Unable to send Hello on down interface %s",
|
||||||
|
@ -1262,6 +1262,7 @@ static void ospf6_ase_calculate_timer(struct event *t)
|
|||||||
* no longer valid.
|
* no longer valid.
|
||||||
*/
|
*/
|
||||||
ospf6_zebra_gr_disable(ospf6);
|
ospf6_zebra_gr_disable(ospf6);
|
||||||
|
ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period);
|
||||||
ospf6->gr_info.finishing_restart = false;
|
ospf6->gr_info.finishing_restart = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -455,6 +455,13 @@ struct ospf6 *ospf6_instance_create(const char *name)
|
|||||||
if (ospf6->router_id == 0)
|
if (ospf6->router_id == 0)
|
||||||
ospf6_router_id_update(ospf6, true);
|
ospf6_router_id_update(ospf6, true);
|
||||||
ospf6_add(ospf6);
|
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) {
|
if (ospf6->vrf_id != VRF_UNKNOWN) {
|
||||||
vrf = vrf_lookup_by_id(ospf6->vrf_id);
|
vrf = vrf_lookup_by_id(ospf6->vrf_id);
|
||||||
FOR_ALL_INTERFACES (vrf, ifp) {
|
FOR_ALL_INTERFACES (vrf, ifp) {
|
||||||
@ -465,12 +472,6 @@ struct ospf6 *ospf6_instance_create(const char *name)
|
|||||||
if (ospf6->fd < 0)
|
if (ospf6->fd < 0)
|
||||||
return ospf6;
|
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,
|
event_add_read(master, ospf6_receive, ospf6, ospf6->fd,
|
||||||
&ospf6->t_ospf6_receive);
|
&ospf6->t_ospf6_receive);
|
||||||
|
|
||||||
@ -490,6 +491,7 @@ void ospf6_delete(struct ospf6 *o)
|
|||||||
ospf6_gr_helper_deinit(o);
|
ospf6_gr_helper_deinit(o);
|
||||||
if (!o->gr_info.prepare_in_progress)
|
if (!o->gr_info.prepare_in_progress)
|
||||||
ospf6_flush_self_originated_lsas_now(o);
|
ospf6_flush_self_originated_lsas_now(o);
|
||||||
|
XFREE(MTYPE_TMP, o->gr_info.exit_reason);
|
||||||
ospf6_disable(o);
|
ospf6_disable(o);
|
||||||
ospf6_del(o);
|
ospf6_del(o);
|
||||||
|
|
||||||
@ -693,6 +695,9 @@ DEFUN(no_router_ospf6, no_router_ospf6_cmd, "no router ospf6 [vrf NAME]",
|
|||||||
if (ospf6 == NULL)
|
if (ospf6 == NULL)
|
||||||
vty_out(vty, "OSPFv3 is not configured\n");
|
vty_out(vty, "OSPFv3 is not configured\n");
|
||||||
else {
|
else {
|
||||||
|
if (ospf6->gr_info.restart_support)
|
||||||
|
ospf6_gr_nvm_delete(ospf6);
|
||||||
|
|
||||||
ospf6_delete(ospf6);
|
ospf6_delete(ospf6);
|
||||||
ospf6 = NULL;
|
ospf6 = NULL;
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,8 @@ struct ospf6_gr_info {
|
|||||||
bool prepare_in_progress;
|
bool prepare_in_progress;
|
||||||
bool finishing_restart;
|
bool finishing_restart;
|
||||||
uint32_t grace_period;
|
uint32_t grace_period;
|
||||||
|
int reason;
|
||||||
|
char *exit_reason;
|
||||||
struct event *t_grace_period;
|
struct event *t_grace_period;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -239,12 +239,18 @@ static int ospf6_zebra_gr_update(struct ospf6 *ospf6, int command,
|
|||||||
|
|
||||||
int ospf6_zebra_gr_enable(struct ospf6 *ospf6, uint32_t stale_time)
|
int ospf6_zebra_gr_enable(struct ospf6 *ospf6, uint32_t stale_time)
|
||||||
{
|
{
|
||||||
|
if (IS_DEBUG_OSPF6_GR)
|
||||||
|
zlog_debug("Zebra enable GR [stale time %u]", stale_time);
|
||||||
|
|
||||||
return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_CAPABILITIES,
|
return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_CAPABILITIES,
|
||||||
stale_time);
|
stale_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ospf6_zebra_gr_disable(struct ospf6 *ospf6)
|
int ospf6_zebra_gr_disable(struct ospf6 *ospf6)
|
||||||
{
|
{
|
||||||
|
if (IS_DEBUG_OSPF6_GR)
|
||||||
|
zlog_debug("Zebra disable GR");
|
||||||
|
|
||||||
return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_DISABLE, 0);
|
return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_DISABLE, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -735,10 +741,20 @@ uint8_t ospf6_distance_apply(struct prefix_ipv6 *p, struct ospf6_route * or,
|
|||||||
|
|
||||||
static void ospf6_zebra_connected(struct zclient *zclient)
|
static void ospf6_zebra_connected(struct zclient *zclient)
|
||||||
{
|
{
|
||||||
|
struct ospf6 *ospf6;
|
||||||
|
struct listnode *node;
|
||||||
|
|
||||||
/* Send the client registration */
|
/* Send the client registration */
|
||||||
bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
|
bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
|
||||||
|
|
||||||
zclient_send_reg_requests(zclient, 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[] = {
|
static zclient_handler *const ospf6_handlers[] = {
|
||||||
|
@ -78,6 +78,7 @@ clippy_scan += \
|
|||||||
ospf6d/ospf6_top.c \
|
ospf6d/ospf6_top.c \
|
||||||
ospf6d/ospf6_area.c \
|
ospf6d/ospf6_area.c \
|
||||||
ospf6d/ospf6_asbr.c \
|
ospf6d/ospf6_asbr.c \
|
||||||
|
ospf6d/ospf6_interface.c \
|
||||||
ospf6d/ospf6_lsa.c \
|
ospf6d/ospf6_lsa.c \
|
||||||
ospf6d/ospf6_gr_helper.c \
|
ospf6d/ospf6_gr_helper.c \
|
||||||
ospf6d/ospf6_gr.c \
|
ospf6d/ospf6_gr.c \
|
||||||
|
@ -615,6 +615,7 @@ static void ospf_ase_calculate_timer(struct event *t)
|
|||||||
*/
|
*/
|
||||||
if (ospf->gr_info.finishing_restart) {
|
if (ospf->gr_info.finishing_restart) {
|
||||||
ospf_zebra_gr_disable(ospf);
|
ospf_zebra_gr_disable(ospf);
|
||||||
|
ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period);
|
||||||
ospf->gr_info.finishing_restart = false;
|
ospf->gr_info.finishing_restart = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
189
ospfd/ospf_gr.c
189
ospfd/ospf_gr.c
@ -33,7 +33,7 @@
|
|||||||
#include "ospfd/ospf_dump.h"
|
#include "ospfd/ospf_dump.h"
|
||||||
#include "ospfd/ospf_gr_clippy.c"
|
#include "ospfd/ospf_gr_clippy.c"
|
||||||
|
|
||||||
static void ospf_gr_nvm_delete(struct ospf *ospf);
|
static void ospf_gr_grace_period_expired(struct event *thread);
|
||||||
|
|
||||||
/* Lookup self-originated Grace-LSA in the LSDB. */
|
/* Lookup self-originated Grace-LSA in the LSDB. */
|
||||||
static struct ospf_lsa *ospf_gr_lsa_lookup(struct ospf *ospf,
|
static struct ospf_lsa *ospf_gr_lsa_lookup(struct ospf *ospf,
|
||||||
@ -53,7 +53,9 @@ static struct ospf_lsa *ospf_gr_lsa_lookup(struct ospf *ospf,
|
|||||||
|
|
||||||
/* Fill in fields of the Grace-LSA that is being originated. */
|
/* Fill in fields of the Grace-LSA that is being originated. */
|
||||||
static void ospf_gr_lsa_body_set(struct ospf_gr_info *gr_info,
|
static void ospf_gr_lsa_body_set(struct ospf_gr_info *gr_info,
|
||||||
struct ospf_interface *oi, struct stream *s)
|
struct ospf_interface *oi,
|
||||||
|
enum ospf_gr_restart_reason reason,
|
||||||
|
struct stream *s)
|
||||||
{
|
{
|
||||||
struct grace_tlv_graceperiod tlv_period = {};
|
struct grace_tlv_graceperiod tlv_period = {};
|
||||||
struct grace_tlv_restart_reason tlv_reason = {};
|
struct grace_tlv_restart_reason tlv_reason = {};
|
||||||
@ -68,10 +70,7 @@ static void ospf_gr_lsa_body_set(struct ospf_gr_info *gr_info,
|
|||||||
/* Put restart reason. */
|
/* Put restart reason. */
|
||||||
tlv_reason.header.type = htons(RESTART_REASON_TYPE);
|
tlv_reason.header.type = htons(RESTART_REASON_TYPE);
|
||||||
tlv_reason.header.length = htons(RESTART_REASON_LENGTH);
|
tlv_reason.header.length = htons(RESTART_REASON_LENGTH);
|
||||||
if (gr_info->restart_support)
|
tlv_reason.reason = reason;
|
||||||
tlv_reason.reason = OSPF_GR_SW_RESTART;
|
|
||||||
else
|
|
||||||
tlv_reason.reason = OSPF_GR_UNKNOWN_RESTART;
|
|
||||||
stream_put(s, &tlv_reason, sizeof(tlv_reason));
|
stream_put(s, &tlv_reason, sizeof(tlv_reason));
|
||||||
|
|
||||||
/* Put IP address. */
|
/* Put IP address. */
|
||||||
@ -85,7 +84,8 @@ static void ospf_gr_lsa_body_set(struct ospf_gr_info *gr_info,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Generate Grace-LSA for a given interface. */
|
/* Generate Grace-LSA for a given interface. */
|
||||||
static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi)
|
static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi,
|
||||||
|
enum ospf_gr_restart_reason reason)
|
||||||
{
|
{
|
||||||
struct stream *s;
|
struct stream *s;
|
||||||
struct lsa_header *lsah;
|
struct lsa_header *lsah;
|
||||||
@ -112,7 +112,7 @@ static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi)
|
|||||||
lsa_header_set(s, options, lsa_type, lsa_id, oi->ospf->router_id);
|
lsa_header_set(s, options, lsa_type, lsa_id, oi->ospf->router_id);
|
||||||
|
|
||||||
/* Set opaque-LSA body fields. */
|
/* Set opaque-LSA body fields. */
|
||||||
ospf_gr_lsa_body_set(&oi->ospf->gr_info, oi, s);
|
ospf_gr_lsa_body_set(&oi->ospf->gr_info, oi, reason, s);
|
||||||
|
|
||||||
/* Set length. */
|
/* Set length. */
|
||||||
length = stream_get_endp(s);
|
length = stream_get_endp(s);
|
||||||
@ -135,15 +135,20 @@ static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Originate and install Grace-LSA for a given interface. */
|
/* Originate and install Grace-LSA for a given interface. */
|
||||||
static void ospf_gr_lsa_originate(struct ospf_interface *oi, bool maxage)
|
static void ospf_gr_lsa_originate(struct ospf_interface *oi,
|
||||||
|
enum ospf_gr_restart_reason reason,
|
||||||
|
bool maxage)
|
||||||
{
|
{
|
||||||
struct ospf_lsa *lsa, *old;
|
struct ospf_lsa *lsa, *old;
|
||||||
|
|
||||||
if (ospf_interface_neighbor_count(oi) == 0)
|
/* Skip originating a Grace-LSA when not necessary. */
|
||||||
|
if (!if_is_operative(oi->ifp) || if_is_loopback(oi->ifp) ||
|
||||||
|
(reason != OSPF_GR_UNKNOWN_RESTART &&
|
||||||
|
ospf_interface_neighbor_count(oi) == 0))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Create new Grace-LSA. */
|
/* Create new Grace-LSA. */
|
||||||
lsa = ospf_gr_lsa_new(oi);
|
lsa = ospf_gr_lsa_new(oi, reason);
|
||||||
if (!lsa) {
|
if (!lsa) {
|
||||||
zlog_warn("%s: ospf_gr_lsa_new() failed", __func__);
|
zlog_warn("%s: ospf_gr_lsa_new() failed", __func__);
|
||||||
return;
|
return;
|
||||||
@ -157,6 +162,23 @@ static void ospf_gr_lsa_originate(struct ospf_interface *oi, bool maxage)
|
|||||||
if (old)
|
if (old)
|
||||||
lsa->data->ls_seqnum = lsa_seqnum_increment(old);
|
lsa->data->ls_seqnum = lsa_seqnum_increment(old);
|
||||||
|
|
||||||
|
if (!maxage && reason == OSPF_GR_UNKNOWN_RESTART) {
|
||||||
|
struct list *update;
|
||||||
|
struct in_addr addr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When performing an unplanned restart, send a handcrafted
|
||||||
|
* Grace-LSA since the interface isn't fully initialized yet.
|
||||||
|
*/
|
||||||
|
ospf_lsa_checksum(lsa->data);
|
||||||
|
ospf_lsa_lock(lsa);
|
||||||
|
update = list_new();
|
||||||
|
listnode_add(update, lsa);
|
||||||
|
addr.s_addr = htonl(OSPF_ALLSPFROUTERS);
|
||||||
|
ospf_ls_upd_queue_send(oi, update, addr, true);
|
||||||
|
list_delete(&update);
|
||||||
|
ospf_lsa_discard(lsa);
|
||||||
|
} else {
|
||||||
/* Install this LSA into LSDB. */
|
/* Install this LSA into LSDB. */
|
||||||
if (ospf_lsa_install(oi->ospf, oi, lsa) == NULL) {
|
if (ospf_lsa_install(oi->ospf, oi, lsa) == NULL) {
|
||||||
zlog_warn("%s: ospf_lsa_install() failed", __func__);
|
zlog_warn("%s: ospf_lsa_install() failed", __func__);
|
||||||
@ -164,13 +186,14 @@ static void ospf_gr_lsa_originate(struct ospf_interface *oi, bool maxage)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update new LSA origination count. */
|
|
||||||
oi->ospf->lsa_originate_count++;
|
|
||||||
|
|
||||||
/* Flood the LSA through out the interface */
|
/* Flood the LSA through out the interface */
|
||||||
ospf_flood_through_interface(oi, NULL, lsa);
|
ospf_flood_through_interface(oi, NULL, lsa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Update new LSA origination count. */
|
||||||
|
oi->ospf->lsa_originate_count++;
|
||||||
|
}
|
||||||
|
|
||||||
/* Flush all self-originated Grace-LSAs. */
|
/* Flush all self-originated Grace-LSAs. */
|
||||||
static void ospf_gr_flush_grace_lsas(struct ospf *ospf)
|
static void ospf_gr_flush_grace_lsas(struct ospf *ospf)
|
||||||
{
|
{
|
||||||
@ -181,13 +204,14 @@ static void ospf_gr_flush_grace_lsas(struct ospf *ospf)
|
|||||||
struct ospf_interface *oi;
|
struct ospf_interface *oi;
|
||||||
struct listnode *inode;
|
struct listnode *inode;
|
||||||
|
|
||||||
|
for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi)) {
|
||||||
if (IS_DEBUG_OSPF_GR)
|
if (IS_DEBUG_OSPF_GR)
|
||||||
zlog_debug(
|
zlog_debug(
|
||||||
"GR: flushing self-originated Grace-LSAs [area %pI4]",
|
"GR: flushing self-originated Grace-LSA [area %pI4] [interface %s]",
|
||||||
&area->area_id);
|
&area->area_id, oi->ifp->name);
|
||||||
|
|
||||||
for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi))
|
ospf_gr_lsa_originate(oi, ospf->gr_info.reason, true);
|
||||||
ospf_gr_lsa_originate(oi, true);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,9 +227,6 @@ static void ospf_gr_restart_exit(struct ospf *ospf, const char *reason)
|
|||||||
ospf->gr_info.restart_in_progress = false;
|
ospf->gr_info.restart_in_progress = false;
|
||||||
EVENT_OFF(ospf->gr_info.t_grace_period);
|
EVENT_OFF(ospf->gr_info.t_grace_period);
|
||||||
|
|
||||||
/* Record in non-volatile memory that the restart is complete. */
|
|
||||||
ospf_gr_nvm_delete(ospf);
|
|
||||||
|
|
||||||
for (ALL_LIST_ELEMENTS_RO(ospf->areas, onode, area)) {
|
for (ALL_LIST_ELEMENTS_RO(ospf->areas, onode, area)) {
|
||||||
struct ospf_interface *oi;
|
struct ospf_interface *oi;
|
||||||
|
|
||||||
@ -216,14 +237,23 @@ static void ospf_gr_restart_exit(struct ospf *ospf, const char *reason)
|
|||||||
*/
|
*/
|
||||||
ospf_router_lsa_update_area(area);
|
ospf_router_lsa_update_area(area);
|
||||||
|
|
||||||
|
for (ALL_LIST_ELEMENTS_RO(area->oiflist, anode, oi)) {
|
||||||
|
/* Disable hello delay. */
|
||||||
|
if (oi->gr.hello_delay.t_grace_send) {
|
||||||
|
oi->gr.hello_delay.elapsed_seconds = 0;
|
||||||
|
EVENT_OFF(oi->gr.hello_delay.t_grace_send);
|
||||||
|
OSPF_ISM_TIMER_MSEC_ON(oi->t_hello,
|
||||||
|
ospf_hello_timer, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 2) The router should reoriginate network-LSAs on all segments
|
* 2) The router should reoriginate network-LSAs on all
|
||||||
* where it is the Designated Router.
|
* segments where it is the Designated Router.
|
||||||
*/
|
*/
|
||||||
for (ALL_LIST_ELEMENTS_RO(area->oiflist, anode, oi))
|
|
||||||
if (oi->state == ISM_DR)
|
if (oi->state == ISM_DR)
|
||||||
ospf_network_lsa_update(oi);
|
ospf_network_lsa_update(oi);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 5) Any received self-originated LSAs that are no longer valid should
|
* 5) Any received self-originated LSAs that are no longer valid should
|
||||||
@ -242,12 +272,34 @@ static void ospf_gr_restart_exit(struct ospf *ospf, const char *reason)
|
|||||||
* should be removed.
|
* should be removed.
|
||||||
*/
|
*/
|
||||||
ospf->gr_info.finishing_restart = true;
|
ospf->gr_info.finishing_restart = true;
|
||||||
|
XFREE(MTYPE_TMP, ospf->gr_info.exit_reason);
|
||||||
|
ospf->gr_info.exit_reason = XSTRDUP(MTYPE_TMP, reason);
|
||||||
ospf_spf_calculate_schedule(ospf, SPF_FLAG_GR_FINISH);
|
ospf_spf_calculate_schedule(ospf, SPF_FLAG_GR_FINISH);
|
||||||
|
|
||||||
/* 6) Any grace-LSAs that the router originated should be flushed. */
|
/* 6) Any grace-LSAs that the router originated should be flushed. */
|
||||||
ospf_gr_flush_grace_lsas(ospf);
|
ospf_gr_flush_grace_lsas(ospf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Enter the Graceful Restart mode. */
|
||||||
|
void ospf_gr_restart_enter(struct ospf *ospf,
|
||||||
|
enum ospf_gr_restart_reason reason, int timestamp)
|
||||||
|
{
|
||||||
|
unsigned long remaining_time;
|
||||||
|
|
||||||
|
ospf->gr_info.restart_in_progress = true;
|
||||||
|
ospf->gr_info.reason = reason;
|
||||||
|
|
||||||
|
/* Schedule grace period timeout. */
|
||||||
|
remaining_time = timestamp - time(NULL);
|
||||||
|
if (IS_DEBUG_OSPF_GR)
|
||||||
|
zlog_debug(
|
||||||
|
"GR: remaining time until grace period expires: %lu(s)",
|
||||||
|
remaining_time);
|
||||||
|
|
||||||
|
event_add_timer(master, ospf_gr_grace_period_expired, ospf,
|
||||||
|
remaining_time, &ospf->gr_info.t_grace_period);
|
||||||
|
}
|
||||||
|
|
||||||
/* Check if a Router-LSA contains a given link. */
|
/* Check if a Router-LSA contains a given link. */
|
||||||
static bool ospf_router_lsa_contains_adj(struct ospf_lsa *lsa,
|
static bool ospf_router_lsa_contains_adj(struct ospf_lsa *lsa,
|
||||||
struct in_addr *id)
|
struct in_addr *id)
|
||||||
@ -518,11 +570,26 @@ static char *ospf_gr_nvm_filepath(struct ospf *ospf)
|
|||||||
return filepath;
|
return filepath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Send extra Grace-LSA out the interface (unplanned outages only). */
|
||||||
|
void ospf_gr_iface_send_grace_lsa(struct event *thread)
|
||||||
|
{
|
||||||
|
struct ospf_interface *oi = EVENT_ARG(thread);
|
||||||
|
struct ospf_if_params *params = IF_DEF_PARAMS(oi->ifp);
|
||||||
|
|
||||||
|
ospf_gr_lsa_originate(oi, oi->ospf->gr_info.reason, false);
|
||||||
|
|
||||||
|
if (++oi->gr.hello_delay.elapsed_seconds < params->v_gr_hello_delay)
|
||||||
|
event_add_timer(master, ospf_gr_iface_send_grace_lsa, oi, 1,
|
||||||
|
&oi->gr.hello_delay.t_grace_send);
|
||||||
|
else
|
||||||
|
OSPF_ISM_TIMER_MSEC_ON(oi->t_hello, ospf_hello_timer, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Record in non-volatile memory that the given OSPF instance is attempting to
|
* Record in non-volatile memory that the given OSPF instance is attempting to
|
||||||
* perform a graceful restart.
|
* perform a graceful restart.
|
||||||
*/
|
*/
|
||||||
static void ospf_gr_nvm_update(struct ospf *ospf)
|
static void ospf_gr_nvm_update(struct ospf *ospf, bool prepare)
|
||||||
{
|
{
|
||||||
char *filepath;
|
char *filepath;
|
||||||
const char *inst_name;
|
const char *inst_name;
|
||||||
@ -550,14 +617,16 @@ static void ospf_gr_nvm_update(struct ospf *ospf)
|
|||||||
json_instance);
|
json_instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
json_object_int_add(json_instance, "gracePeriod",
|
||||||
|
ospf->gr_info.grace_period);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Record not only the grace period, but also a UNIX timestamp
|
* Record not only the grace period, but also a UNIX timestamp
|
||||||
* corresponding to the end of that period. That way, once ospfd is
|
* corresponding to the end of that period. That way, once ospfd is
|
||||||
* restarted, it will be possible to take into account the time that
|
* restarted, it will be possible to take into account the time that
|
||||||
* passed while ospfd wasn't running.
|
* passed while ospfd wasn't running.
|
||||||
*/
|
*/
|
||||||
json_object_int_add(json_instance, "gracePeriod",
|
if (prepare)
|
||||||
ospf->gr_info.grace_period);
|
|
||||||
json_object_int_add(json_instance, "timestamp",
|
json_object_int_add(json_instance, "timestamp",
|
||||||
time(NULL) + ospf->gr_info.grace_period);
|
time(NULL) + ospf->gr_info.grace_period);
|
||||||
|
|
||||||
@ -569,7 +638,7 @@ static void ospf_gr_nvm_update(struct ospf *ospf)
|
|||||||
* Delete GR status information about the given OSPF instance from non-volatile
|
* Delete GR status information about the given OSPF instance from non-volatile
|
||||||
* memory.
|
* memory.
|
||||||
*/
|
*/
|
||||||
static void ospf_gr_nvm_delete(struct ospf *ospf)
|
void ospf_gr_nvm_delete(struct ospf *ospf)
|
||||||
{
|
{
|
||||||
char *filepath;
|
char *filepath;
|
||||||
const char *inst_name;
|
const char *inst_name;
|
||||||
@ -607,6 +676,7 @@ void ospf_gr_nvm_read(struct ospf *ospf)
|
|||||||
json_object *json_instances;
|
json_object *json_instances;
|
||||||
json_object *json_instance;
|
json_object *json_instance;
|
||||||
json_object *json_timestamp;
|
json_object *json_timestamp;
|
||||||
|
json_object *json_grace_period;
|
||||||
time_t timestamp = 0;
|
time_t timestamp = 0;
|
||||||
|
|
||||||
filepath = ospf_gr_nvm_filepath(ospf);
|
filepath = ospf_gr_nvm_filepath(ospf);
|
||||||
@ -629,29 +699,33 @@ void ospf_gr_nvm_read(struct ospf *ospf)
|
|||||||
json_instance);
|
json_instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
json_object_object_get_ex(json_instance, "gracePeriod",
|
||||||
|
&json_grace_period);
|
||||||
json_object_object_get_ex(json_instance, "timestamp", &json_timestamp);
|
json_object_object_get_ex(json_instance, "timestamp", &json_timestamp);
|
||||||
|
|
||||||
if (json_timestamp) {
|
if (json_timestamp) {
|
||||||
time_t now;
|
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);
|
now = time(NULL);
|
||||||
timestamp = json_object_get_int(json_timestamp);
|
timestamp = json_object_get_int(json_timestamp);
|
||||||
if (now > timestamp) {
|
if (now > timestamp) {
|
||||||
ospf_gr_restart_exit(
|
ospf_gr_restart_exit(
|
||||||
ospf, "grace period has expired already");
|
ospf, "grace period has expired already");
|
||||||
} else {
|
} else
|
||||||
/* Schedule grace period timeout. */
|
ospf_gr_restart_enter(ospf, OSPF_GR_SW_RESTART,
|
||||||
ospf->gr_info.restart_in_progress = true;
|
timestamp);
|
||||||
remaining_time = timestamp - time(NULL);
|
} else if (json_grace_period) {
|
||||||
if (IS_DEBUG_OSPF_GR)
|
uint32_t grace_period;
|
||||||
zlog_debug(
|
|
||||||
"GR: remaining time until grace period expires: %lu(s)",
|
/*
|
||||||
remaining_time);
|
* Unplanned GR: the Grace-LSAs will be sent later as soon as
|
||||||
event_add_timer(master, ospf_gr_grace_period_expired,
|
* the interfaces are operational.
|
||||||
ospf, remaining_time,
|
*/
|
||||||
&ospf->gr_info.t_grace_period);
|
grace_period = json_object_get_int(json_grace_period);
|
||||||
}
|
ospf->gr_info.grace_period = grace_period;
|
||||||
|
ospf_gr_restart_enter(ospf, OSPF_GR_UNKNOWN_RESTART,
|
||||||
|
time(NULL) + ospf->gr_info.grace_period);
|
||||||
}
|
}
|
||||||
|
|
||||||
json_object_object_del(json_instances, inst_name);
|
json_object_object_del(json_instances, inst_name);
|
||||||
@ -660,6 +734,17 @@ void ospf_gr_nvm_read(struct ospf *ospf)
|
|||||||
json_object_free(json);
|
json_object_free(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ospf_gr_unplanned_start_interface(struct ospf_interface *oi)
|
||||||
|
{
|
||||||
|
/* Send Grace-LSA. */
|
||||||
|
ospf_gr_lsa_originate(oi, oi->ospf->gr_info.reason, false);
|
||||||
|
|
||||||
|
/* Start GR hello-delay interval. */
|
||||||
|
oi->gr.hello_delay.elapsed_seconds = 0;
|
||||||
|
event_add_timer(master, ospf_gr_iface_send_grace_lsa, oi, 1,
|
||||||
|
&oi->gr.hello_delay.t_grace_send);
|
||||||
|
}
|
||||||
|
|
||||||
/* Prepare to start a Graceful Restart. */
|
/* Prepare to start a Graceful Restart. */
|
||||||
static void ospf_gr_prepare(void)
|
static void ospf_gr_prepare(void)
|
||||||
{
|
{
|
||||||
@ -687,20 +772,12 @@ static void ospf_gr_prepare(void)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Freeze OSPF routes in the RIB. */
|
|
||||||
if (ospf_zebra_gr_enable(ospf, ospf->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. */
|
/* Send a Grace-LSA to all neighbors. */
|
||||||
for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, inode, oi))
|
for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, inode, oi))
|
||||||
ospf_gr_lsa_originate(oi, false);
|
ospf_gr_lsa_originate(oi, OSPF_GR_SW_RESTART, false);
|
||||||
|
|
||||||
/* Record end of the grace period in non-volatile memory. */
|
/* Record end of the grace period in non-volatile memory. */
|
||||||
ospf_gr_nvm_update(ospf);
|
ospf_gr_nvm_update(ospf, true);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Mark that a Graceful Restart preparation is in progress, to
|
* Mark that a Graceful Restart preparation is in progress, to
|
||||||
@ -749,6 +826,12 @@ DEFPY(graceful_restart, graceful_restart_cmd,
|
|||||||
ospf->gr_info.restart_support = true;
|
ospf->gr_info.restart_support = true;
|
||||||
ospf->gr_info.grace_period = grace_period;
|
ospf->gr_info.grace_period = grace_period;
|
||||||
|
|
||||||
|
/* Freeze OSPF routes in the RIB. */
|
||||||
|
(void)ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period);
|
||||||
|
|
||||||
|
/* Record that GR is enabled in non-volatile memory. */
|
||||||
|
ospf_gr_nvm_update(ospf, false);
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -771,6 +854,8 @@ DEFPY(no_graceful_restart, no_graceful_restart_cmd,
|
|||||||
|
|
||||||
ospf->gr_info.restart_support = false;
|
ospf->gr_info.restart_support = false;
|
||||||
ospf->gr_info.grace_period = OSPF_DFLT_GRACE_INTERVAL;
|
ospf->gr_info.grace_period = OSPF_DFLT_GRACE_INTERVAL;
|
||||||
|
ospf_gr_nvm_delete(ospf);
|
||||||
|
ospf_zebra_gr_disable(ospf);
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -166,11 +166,16 @@ extern void ospf_gr_helper_supported_gracetime_set(struct ospf *ospf,
|
|||||||
uint32_t interval);
|
uint32_t interval);
|
||||||
extern void ospf_gr_helper_set_supported_planned_only_restart(struct ospf *ospf,
|
extern void ospf_gr_helper_set_supported_planned_only_restart(struct ospf *ospf,
|
||||||
bool planned_only);
|
bool planned_only);
|
||||||
|
extern void ospf_gr_iface_send_grace_lsa(struct event *thread);
|
||||||
|
extern void ospf_gr_restart_enter(struct ospf *ospf,
|
||||||
|
enum ospf_gr_restart_reason reason,
|
||||||
|
int timestamp);
|
||||||
extern void ospf_gr_check_lsdb_consistency(struct ospf *ospf,
|
extern void ospf_gr_check_lsdb_consistency(struct ospf *ospf,
|
||||||
struct ospf_area *area);
|
struct ospf_area *area);
|
||||||
extern void ospf_gr_check_adjs(struct ospf *ospf);
|
extern void ospf_gr_check_adjs(struct ospf *ospf);
|
||||||
extern void ospf_gr_nvm_read(struct ospf *ospf);
|
extern void ospf_gr_nvm_read(struct ospf *ospf);
|
||||||
|
extern void ospf_gr_nvm_delete(struct ospf *ospf);
|
||||||
|
extern void ospf_gr_unplanned_start_interface(struct ospf_interface *oi);
|
||||||
extern void ospf_gr_init(void);
|
extern void ospf_gr_init(void);
|
||||||
|
|
||||||
#endif /* _ZEBRA_OSPF_GR_H */
|
#endif /* _ZEBRA_OSPF_GR_H */
|
||||||
|
@ -530,6 +530,7 @@ static struct ospf_if_params *ospf_new_if_params(void)
|
|||||||
UNSET_IF_PARAM(oip, passive_interface);
|
UNSET_IF_PARAM(oip, passive_interface);
|
||||||
UNSET_IF_PARAM(oip, v_hello);
|
UNSET_IF_PARAM(oip, v_hello);
|
||||||
UNSET_IF_PARAM(oip, fast_hello);
|
UNSET_IF_PARAM(oip, fast_hello);
|
||||||
|
UNSET_IF_PARAM(oip, v_gr_hello_delay);
|
||||||
UNSET_IF_PARAM(oip, v_wait);
|
UNSET_IF_PARAM(oip, v_wait);
|
||||||
UNSET_IF_PARAM(oip, priority);
|
UNSET_IF_PARAM(oip, priority);
|
||||||
UNSET_IF_PARAM(oip, type);
|
UNSET_IF_PARAM(oip, type);
|
||||||
@ -679,6 +680,9 @@ int ospf_if_new_hook(struct interface *ifp)
|
|||||||
SET_IF_PARAM(IF_DEF_PARAMS(ifp), fast_hello);
|
SET_IF_PARAM(IF_DEF_PARAMS(ifp), fast_hello);
|
||||||
IF_DEF_PARAMS(ifp)->fast_hello = OSPF_FAST_HELLO_DEFAULT;
|
IF_DEF_PARAMS(ifp)->fast_hello = OSPF_FAST_HELLO_DEFAULT;
|
||||||
|
|
||||||
|
SET_IF_PARAM(IF_DEF_PARAMS(ifp), v_gr_hello_delay);
|
||||||
|
IF_DEF_PARAMS(ifp)->v_gr_hello_delay = OSPF_HELLO_DELAY_DEFAULT;
|
||||||
|
|
||||||
SET_IF_PARAM(IF_DEF_PARAMS(ifp), v_wait);
|
SET_IF_PARAM(IF_DEF_PARAMS(ifp), v_wait);
|
||||||
IF_DEF_PARAMS(ifp)->v_wait = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT;
|
IF_DEF_PARAMS(ifp)->v_wait = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT;
|
||||||
|
|
||||||
|
@ -72,6 +72,9 @@ struct ospf_if_params {
|
|||||||
DECLARE_IF_PARAM(uint32_t, v_wait); /* Router Dead Interval */
|
DECLARE_IF_PARAM(uint32_t, v_wait); /* Router Dead Interval */
|
||||||
bool is_v_wait_set; /* Check for Dead Interval set */
|
bool is_v_wait_set; /* Check for Dead Interval set */
|
||||||
|
|
||||||
|
/* GR Hello Delay Interval */
|
||||||
|
DECLARE_IF_PARAM(uint16_t, v_gr_hello_delay);
|
||||||
|
|
||||||
/* MTU mismatch check (see RFC2328, chap 10.6) */
|
/* MTU mismatch check (see RFC2328, chap 10.6) */
|
||||||
DECLARE_IF_PARAM(uint8_t, mtu_ignore);
|
DECLARE_IF_PARAM(uint8_t, mtu_ignore);
|
||||||
|
|
||||||
@ -214,6 +217,14 @@ struct ospf_interface {
|
|||||||
/* List of configured NBMA neighbor. */
|
/* List of configured NBMA neighbor. */
|
||||||
struct list *nbr_nbma;
|
struct list *nbr_nbma;
|
||||||
|
|
||||||
|
/* Graceful-Restart data. */
|
||||||
|
struct {
|
||||||
|
struct {
|
||||||
|
uint16_t elapsed_seconds;
|
||||||
|
struct event *t_grace_send;
|
||||||
|
} hello_delay;
|
||||||
|
} gr;
|
||||||
|
|
||||||
/* self-originated LSAs. */
|
/* self-originated LSAs. */
|
||||||
struct ospf_lsa *network_lsa_self; /* network-LSA. */
|
struct ospf_lsa *network_lsa_self; /* network-LSA. */
|
||||||
struct list *opaque_lsa_self; /* Type-9 Opaque-LSAs */
|
struct list *opaque_lsa_self; /* Type-9 Opaque-LSAs */
|
||||||
|
@ -244,6 +244,10 @@ void ospf_hello_timer(struct event *thread)
|
|||||||
oi = EVENT_ARG(thread);
|
oi = EVENT_ARG(thread);
|
||||||
oi->t_hello = NULL;
|
oi->t_hello = NULL;
|
||||||
|
|
||||||
|
/* Check if the GR hello-delay is active. */
|
||||||
|
if (oi->gr.hello_delay.t_grace_send)
|
||||||
|
return;
|
||||||
|
|
||||||
if (IS_DEBUG_OSPF(ism, ISM_TIMERS))
|
if (IS_DEBUG_OSPF(ism, ISM_TIMERS))
|
||||||
zlog_debug("ISM[%s]: Timer (Hello timer expire)", IF_NAME(oi));
|
zlog_debug("ISM[%s]: Timer (Hello timer expire)", IF_NAME(oi));
|
||||||
|
|
||||||
@ -282,6 +286,7 @@ static void ism_timer_set(struct ospf_interface *oi)
|
|||||||
EVENT_OFF(oi->t_hello);
|
EVENT_OFF(oi->t_hello);
|
||||||
EVENT_OFF(oi->t_wait);
|
EVENT_OFF(oi->t_wait);
|
||||||
EVENT_OFF(oi->t_ls_ack);
|
EVENT_OFF(oi->t_ls_ack);
|
||||||
|
EVENT_OFF(oi->gr.hello_delay.t_grace_send);
|
||||||
break;
|
break;
|
||||||
case ISM_Loopback:
|
case ISM_Loopback:
|
||||||
/* In this state, the interface may be looped back and will be
|
/* In this state, the interface may be looped back and will be
|
||||||
@ -289,6 +294,7 @@ static void ism_timer_set(struct ospf_interface *oi)
|
|||||||
EVENT_OFF(oi->t_hello);
|
EVENT_OFF(oi->t_hello);
|
||||||
EVENT_OFF(oi->t_wait);
|
EVENT_OFF(oi->t_wait);
|
||||||
EVENT_OFF(oi->t_ls_ack);
|
EVENT_OFF(oi->t_ls_ack);
|
||||||
|
EVENT_OFF(oi->gr.hello_delay.t_grace_send);
|
||||||
break;
|
break;
|
||||||
case ISM_Waiting:
|
case ISM_Waiting:
|
||||||
/* The router is trying to determine the identity of DRouter and
|
/* The router is trying to determine the identity of DRouter and
|
||||||
|
@ -4040,9 +4040,8 @@ static struct ospf_packet *ospf_ls_upd_packet_new(struct list *update,
|
|||||||
return ospf_packet_new(size - sizeof(struct ip));
|
return ospf_packet_new(size - sizeof(struct ip));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ospf_ls_upd_queue_send(struct ospf_interface *oi,
|
void ospf_ls_upd_queue_send(struct ospf_interface *oi, struct list *update,
|
||||||
struct list *update, struct in_addr addr,
|
struct in_addr addr, int send_lsupd_now)
|
||||||
int send_lsupd_now)
|
|
||||||
{
|
{
|
||||||
struct ospf_packet *op;
|
struct ospf_packet *op;
|
||||||
uint16_t length = OSPF_HEADER_SIZE;
|
uint16_t length = OSPF_HEADER_SIZE;
|
||||||
|
@ -132,6 +132,9 @@ extern void ospf_ls_req_send(struct ospf_neighbor *);
|
|||||||
extern void ospf_ls_upd_send_lsa(struct ospf_neighbor *, struct ospf_lsa *,
|
extern void ospf_ls_upd_send_lsa(struct ospf_neighbor *, struct ospf_lsa *,
|
||||||
int);
|
int);
|
||||||
extern void ospf_ls_upd_send(struct ospf_neighbor *, struct list *, int, int);
|
extern void ospf_ls_upd_send(struct ospf_neighbor *, struct list *, int, int);
|
||||||
|
extern void ospf_ls_upd_queue_send(struct ospf_interface *oi,
|
||||||
|
struct list *update, struct in_addr addr,
|
||||||
|
int send_lsupd_now);
|
||||||
extern void ospf_ls_ack_send(struct ospf_neighbor *, struct ospf_lsa *);
|
extern void ospf_ls_ack_send(struct ospf_neighbor *, struct ospf_lsa *);
|
||||||
extern void ospf_ls_ack_send_delayed(struct ospf_interface *);
|
extern void ospf_ls_ack_send_delayed(struct ospf_interface *);
|
||||||
extern void ospf_ls_retransmit(struct ospf_interface *, struct ospf_lsa *);
|
extern void ospf_ls_retransmit(struct ospf_interface *, struct ospf_lsa *);
|
||||||
|
@ -232,9 +232,12 @@ DEFUN (no_router_ospf,
|
|||||||
return CMD_NOT_MY_INSTANCE;
|
return CMD_NOT_MY_INSTANCE;
|
||||||
|
|
||||||
ospf = ospf_lookup(instance, vrf_name);
|
ospf = ospf_lookup(instance, vrf_name);
|
||||||
if (ospf)
|
if (ospf) {
|
||||||
|
if (ospf->gr_info.restart_support)
|
||||||
|
ospf_gr_nvm_delete(ospf);
|
||||||
|
|
||||||
ospf_finish(ospf);
|
ospf_finish(ospf);
|
||||||
else
|
} else
|
||||||
ret = CMD_WARNING_CONFIG_FAILED;
|
ret = CMD_WARNING_CONFIG_FAILED;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -3617,6 +3620,7 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf,
|
|||||||
struct ospf_neighbor *nbr;
|
struct ospf_neighbor *nbr;
|
||||||
struct route_node *rn;
|
struct route_node *rn;
|
||||||
uint32_t bandwidth = ifp->bandwidth ? ifp->bandwidth : ifp->speed;
|
uint32_t bandwidth = ifp->bandwidth ? ifp->bandwidth : ifp->speed;
|
||||||
|
struct ospf_if_params *params;
|
||||||
|
|
||||||
/* Is interface up? */
|
/* Is interface up? */
|
||||||
if (use_json) {
|
if (use_json) {
|
||||||
@ -3936,6 +3940,20 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf,
|
|||||||
ospf_nbr_count(oi, 0),
|
ospf_nbr_count(oi, 0),
|
||||||
ospf_nbr_count(oi, NSM_Full));
|
ospf_nbr_count(oi, NSM_Full));
|
||||||
|
|
||||||
|
|
||||||
|
params = IF_DEF_PARAMS(ifp);
|
||||||
|
if (params &&
|
||||||
|
OSPF_IF_PARAM_CONFIGURED(params, v_gr_hello_delay)) {
|
||||||
|
if (use_json) {
|
||||||
|
json_object_int_add(json_interface_sub,
|
||||||
|
"grHelloDelaySecs",
|
||||||
|
params->v_gr_hello_delay);
|
||||||
|
} else
|
||||||
|
vty_out(vty,
|
||||||
|
" Graceful Restart hello delay: %us\n",
|
||||||
|
params->v_gr_hello_delay);
|
||||||
|
}
|
||||||
|
|
||||||
ospf_interface_bfd_show(vty, ifp, json_interface_sub);
|
ospf_interface_bfd_show(vty, ifp, json_interface_sub);
|
||||||
|
|
||||||
/* OSPF Authentication information */
|
/* OSPF Authentication information */
|
||||||
@ -8639,6 +8657,59 @@ DEFUN_HIDDEN (no_ospf_retransmit_interval,
|
|||||||
return no_ip_ospf_retransmit_interval(self, vty, argc, argv);
|
return no_ip_ospf_retransmit_interval(self, vty, argc, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFPY (ip_ospf_gr_hdelay,
|
||||||
|
ip_ospf_gr_hdelay_cmd,
|
||||||
|
"ip ospf graceful-restart hello-delay (1-1800)",
|
||||||
|
IP_STR
|
||||||
|
"OSPF interface commands\n"
|
||||||
|
"Graceful Restart parameters\n"
|
||||||
|
"Delay the sending of the first hello packets.\n"
|
||||||
|
"Delay in seconds\n")
|
||||||
|
{
|
||||||
|
VTY_DECLVAR_CONTEXT(interface, ifp);
|
||||||
|
struct ospf_if_params *params;
|
||||||
|
|
||||||
|
params = IF_DEF_PARAMS(ifp);
|
||||||
|
|
||||||
|
/* Note: new or updated value won't affect ongoing graceful restart. */
|
||||||
|
SET_IF_PARAM(params, v_gr_hello_delay);
|
||||||
|
params->v_gr_hello_delay = hello_delay;
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFPY (no_ip_ospf_gr_hdelay,
|
||||||
|
no_ip_ospf_gr_hdelay_cmd,
|
||||||
|
"no ip ospf graceful-restart hello-delay [(1-1800)]",
|
||||||
|
NO_STR
|
||||||
|
IP_STR
|
||||||
|
"OSPF interface commands\n"
|
||||||
|
"Graceful Restart parameters\n"
|
||||||
|
"Delay the sending of the first hello packets.\n"
|
||||||
|
"Delay in seconds\n")
|
||||||
|
{
|
||||||
|
VTY_DECLVAR_CONTEXT(interface, ifp);
|
||||||
|
struct ospf_if_params *params;
|
||||||
|
struct route_node *rn;
|
||||||
|
|
||||||
|
params = IF_DEF_PARAMS(ifp);
|
||||||
|
UNSET_IF_PARAM(params, v_gr_hello_delay);
|
||||||
|
params->v_gr_hello_delay = OSPF_HELLO_DELAY_DEFAULT;
|
||||||
|
|
||||||
|
for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) {
|
||||||
|
struct ospf_interface *oi;
|
||||||
|
|
||||||
|
oi = rn->info;
|
||||||
|
if (!oi)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
oi->gr.hello_delay.elapsed_seconds = 0;
|
||||||
|
EVENT_OFF(oi->gr.hello_delay.t_grace_send);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
DEFUN (ip_ospf_transmit_delay,
|
DEFUN (ip_ospf_transmit_delay,
|
||||||
ip_ospf_transmit_delay_addr_cmd,
|
ip_ospf_transmit_delay_addr_cmd,
|
||||||
"ip ospf transmit-delay (1-65535) [A.B.C.D]",
|
"ip ospf transmit-delay (1-65535) [A.B.C.D]",
|
||||||
@ -11831,6 +11902,15 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf)
|
|||||||
vty_out(vty, "\n");
|
vty_out(vty, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Hello Graceful-Restart Delay print. */
|
||||||
|
if (OSPF_IF_PARAM_CONFIGURED(params,
|
||||||
|
v_gr_hello_delay) &&
|
||||||
|
params->v_gr_hello_delay !=
|
||||||
|
OSPF_HELLO_DELAY_DEFAULT)
|
||||||
|
vty_out(vty,
|
||||||
|
" ip ospf graceful-restart hello-delay %u\n",
|
||||||
|
params->v_gr_hello_delay);
|
||||||
|
|
||||||
/* Router Priority print. */
|
/* Router Priority print. */
|
||||||
if (OSPF_IF_PARAM_CONFIGURED(params, priority)
|
if (OSPF_IF_PARAM_CONFIGURED(params, priority)
|
||||||
&& params->priority
|
&& params->priority
|
||||||
|
@ -1252,12 +1252,18 @@ static int ospf_zebra_gr_update(struct ospf *ospf, int command,
|
|||||||
|
|
||||||
int ospf_zebra_gr_enable(struct ospf *ospf, uint32_t stale_time)
|
int ospf_zebra_gr_enable(struct ospf *ospf, uint32_t stale_time)
|
||||||
{
|
{
|
||||||
|
if (IS_DEBUG_OSPF_GR)
|
||||||
|
zlog_debug("Zebra enable GR [stale time %u]", stale_time);
|
||||||
|
|
||||||
return ospf_zebra_gr_update(ospf, ZEBRA_CLIENT_GR_CAPABILITIES,
|
return ospf_zebra_gr_update(ospf, ZEBRA_CLIENT_GR_CAPABILITIES,
|
||||||
stale_time);
|
stale_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ospf_zebra_gr_disable(struct ospf *ospf)
|
int ospf_zebra_gr_disable(struct ospf *ospf)
|
||||||
{
|
{
|
||||||
|
if (IS_DEBUG_OSPF_GR)
|
||||||
|
zlog_debug("Zebra disable GR");
|
||||||
|
|
||||||
return ospf_zebra_gr_update(ospf, ZEBRA_CLIENT_GR_DISABLE, 0);
|
return ospf_zebra_gr_update(ospf, ZEBRA_CLIENT_GR_DISABLE, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2120,10 +2126,20 @@ int ospf_zebra_label_manager_connect(void)
|
|||||||
|
|
||||||
static void ospf_zebra_connected(struct zclient *zclient)
|
static void ospf_zebra_connected(struct zclient *zclient)
|
||||||
{
|
{
|
||||||
|
struct ospf *ospf;
|
||||||
|
struct listnode *node;
|
||||||
|
|
||||||
/* Send the client registration */
|
/* Send the client registration */
|
||||||
bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
|
bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
|
||||||
|
|
||||||
zclient_send_reg_requests(zclient, VRF_DEFAULT);
|
zclient_send_reg_requests(zclient, VRF_DEFAULT);
|
||||||
|
|
||||||
|
/* Activate graceful restart if configured. */
|
||||||
|
for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) {
|
||||||
|
if (!ospf->gr_info.restart_support)
|
||||||
|
continue;
|
||||||
|
(void)ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -720,6 +720,7 @@ static void ospf_finish_final(struct ospf *ospf)
|
|||||||
|
|
||||||
if (!ospf->gr_info.prepare_in_progress)
|
if (!ospf->gr_info.prepare_in_progress)
|
||||||
ospf_flush_self_originated_lsas_now(ospf);
|
ospf_flush_self_originated_lsas_now(ospf);
|
||||||
|
XFREE(MTYPE_TMP, ospf->gr_info.exit_reason);
|
||||||
|
|
||||||
/* Unregister redistribution */
|
/* Unregister redistribution */
|
||||||
for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
|
for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
|
||||||
@ -1131,6 +1132,17 @@ struct ospf_interface *add_ospf_interface(struct connected *co,
|
|||||||
&& if_is_operative(co->ifp))
|
&& if_is_operative(co->ifp))
|
||||||
ospf_if_up(oi);
|
ospf_if_up(oi);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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->ospf->gr_info.restart_in_progress &&
|
||||||
|
oi->ospf->gr_info.reason == OSPF_GR_UNKNOWN_RESTART)
|
||||||
|
ospf_gr_unplanned_start_interface(oi);
|
||||||
|
|
||||||
return oi;
|
return oi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,6 +148,8 @@ struct ospf_gr_info {
|
|||||||
bool prepare_in_progress;
|
bool prepare_in_progress;
|
||||||
bool finishing_restart;
|
bool finishing_restart;
|
||||||
uint32_t grace_period;
|
uint32_t grace_period;
|
||||||
|
int reason;
|
||||||
|
char *exit_reason;
|
||||||
struct event *t_grace_period;
|
struct event *t_grace_period;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ r1-eth0 is up
|
|||||||
Timer intervals configured, Hello 1s, Dead 5s, Wait 5s, Retransmit 5
|
Timer intervals configured, Hello 1s, Dead 5s, Wait 5s, Retransmit 5
|
||||||
Hello due in XX.XXXs
|
Hello due in XX.XXXs
|
||||||
Neighbor Count is 0, Adjacent neighbor count is 0
|
Neighbor Count is 0, Adjacent neighbor count is 0
|
||||||
|
Graceful Restart hello delay: 10s
|
||||||
r1-eth3 is up
|
r1-eth3 is up
|
||||||
ifindex X, MTU 1500 bytes, BW XX Mbit <UP,BROADCAST,RUNNING,MULTICAST>
|
ifindex X, MTU 1500 bytes, BW XX Mbit <UP,BROADCAST,RUNNING,MULTICAST>
|
||||||
Internet Address 192.168.3.1/26, Broadcast 192.168.3.63, Area 0.0.0.0
|
Internet Address 192.168.3.1/26, Broadcast 192.168.3.63, Area 0.0.0.0
|
||||||
@ -22,3 +23,4 @@ r1-eth3 is up
|
|||||||
Timer intervals configured, Hello 1s, Dead 5s, Wait 5s, Retransmit 5
|
Timer intervals configured, Hello 1s, Dead 5s, Wait 5s, Retransmit 5
|
||||||
Hello due in XX.XXXs
|
Hello due in XX.XXXs
|
||||||
Neighbor Count is 0, Adjacent neighbor count is 0
|
Neighbor Count is 0, Adjacent neighbor count is 0
|
||||||
|
Graceful Restart hello delay: 10s
|
||||||
|
@ -430,6 +430,144 @@ def test_gr_rt7():
|
|||||||
check_routers(restarting="rt7")
|
check_routers(restarting="rt7")
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test rt1 performing an unplanned graceful restart
|
||||||
|
#
|
||||||
|
def test_unplanned_gr_rt1():
|
||||||
|
logger.info("Test: verify rt1 performing an unplanned graceful restart")
|
||||||
|
tgen = get_topogen()
|
||||||
|
|
||||||
|
# Skip if previous fatal error condition is raised
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
kill_router_daemons(tgen, "rt1", ["ospf6d"], save_config=False)
|
||||||
|
start_router_daemons(tgen, "rt1", ["ospf6d"])
|
||||||
|
|
||||||
|
expect_grace_lsa(restarting="1.1.1.1", helper="rt2")
|
||||||
|
ensure_gr_is_in_zebra("rt1")
|
||||||
|
check_routers(restarting="rt1")
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test rt2 performing an unplanned graceful restart
|
||||||
|
#
|
||||||
|
def test_unplanned_gr_rt2():
|
||||||
|
logger.info("Test: verify rt2 performing an unplanned graceful restart")
|
||||||
|
tgen = get_topogen()
|
||||||
|
|
||||||
|
# Skip if previous fatal error condition is raised
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
kill_router_daemons(tgen, "rt2", ["ospf6d"], save_config=False)
|
||||||
|
start_router_daemons(tgen, "rt2", ["ospf6d"])
|
||||||
|
|
||||||
|
expect_grace_lsa(restarting="2.2.2.2", helper="rt1")
|
||||||
|
expect_grace_lsa(restarting="2.2.2.2", helper="rt3")
|
||||||
|
ensure_gr_is_in_zebra("rt2")
|
||||||
|
check_routers(restarting="rt2")
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test rt3 performing an unplanned graceful restart
|
||||||
|
#
|
||||||
|
def test_unplanned_gr_rt3():
|
||||||
|
logger.info("Test: verify rt3 performing an unplanned graceful restart")
|
||||||
|
tgen = get_topogen()
|
||||||
|
|
||||||
|
# Skip if previous fatal error condition is raised
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
kill_router_daemons(tgen, "rt3", ["ospf6d"], save_config=False)
|
||||||
|
start_router_daemons(tgen, "rt3", ["ospf6d"])
|
||||||
|
|
||||||
|
expect_grace_lsa(restarting="3.3.3.3", helper="rt2")
|
||||||
|
expect_grace_lsa(restarting="3.3.3.3", helper="rt4")
|
||||||
|
expect_grace_lsa(restarting="3.3.3.3", helper="rt6")
|
||||||
|
ensure_gr_is_in_zebra("rt3")
|
||||||
|
check_routers(restarting="rt3")
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test rt4 performing an unplanned graceful restart
|
||||||
|
#
|
||||||
|
def test_unplanned_gr_rt4():
|
||||||
|
logger.info("Test: verify rt4 performing an unplanned graceful restart")
|
||||||
|
tgen = get_topogen()
|
||||||
|
|
||||||
|
# Skip if previous fatal error condition is raised
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
kill_router_daemons(tgen, "rt4", ["ospf6d"], save_config=False)
|
||||||
|
start_router_daemons(tgen, "rt4", ["ospf6d"])
|
||||||
|
|
||||||
|
expect_grace_lsa(restarting="4.4.4.4", helper="rt3")
|
||||||
|
expect_grace_lsa(restarting="4.4.4.4", helper="rt5")
|
||||||
|
ensure_gr_is_in_zebra("rt4")
|
||||||
|
check_routers(restarting="rt4")
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test rt5 performing an unplanned graceful restart
|
||||||
|
#
|
||||||
|
def test_unplanned_gr_rt5():
|
||||||
|
logger.info("Test: verify rt5 performing an unplanned graceful restart")
|
||||||
|
tgen = get_topogen()
|
||||||
|
|
||||||
|
# Skip if previous fatal error condition is raised
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
kill_router_daemons(tgen, "rt5", ["ospf6d"], save_config=False)
|
||||||
|
start_router_daemons(tgen, "rt5", ["ospf6d"])
|
||||||
|
|
||||||
|
expect_grace_lsa(restarting="5.5.5.5", helper="rt4")
|
||||||
|
ensure_gr_is_in_zebra("rt5")
|
||||||
|
check_routers(restarting="rt5")
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test rt6 performing an unplanned graceful restart
|
||||||
|
#
|
||||||
|
def test_unplanned_gr_rt6():
|
||||||
|
logger.info("Test: verify rt6 performing an unplanned graceful restart")
|
||||||
|
tgen = get_topogen()
|
||||||
|
|
||||||
|
# Skip if previous fatal error condition is raised
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
kill_router_daemons(tgen, "rt6", ["ospf6d"], save_config=False)
|
||||||
|
start_router_daemons(tgen, "rt6", ["ospf6d"])
|
||||||
|
|
||||||
|
expect_grace_lsa(restarting="6.6.6.6", helper="rt3")
|
||||||
|
expect_grace_lsa(restarting="6.6.6.6", helper="rt7")
|
||||||
|
ensure_gr_is_in_zebra("rt6")
|
||||||
|
check_routers(restarting="rt6")
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test rt7 performing an unplanned graceful restart
|
||||||
|
#
|
||||||
|
def test_unplanned_gr_rt7():
|
||||||
|
logger.info("Test: verify rt7 performing an unplanned graceful restart")
|
||||||
|
tgen = get_topogen()
|
||||||
|
|
||||||
|
# Skip if previous fatal error condition is raised
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
kill_router_daemons(tgen, "rt7", ["ospf6d"], save_config=False)
|
||||||
|
start_router_daemons(tgen, "rt7", ["ospf6d"])
|
||||||
|
|
||||||
|
expect_grace_lsa(restarting="6.6.6.6", helper="rt6")
|
||||||
|
ensure_gr_is_in_zebra("rt7")
|
||||||
|
check_routers(restarting="rt7")
|
||||||
|
|
||||||
|
|
||||||
# Memory leak test template
|
# Memory leak test template
|
||||||
def test_memory_leak():
|
def test_memory_leak():
|
||||||
"Run the memory leak test and report results."
|
"Run the memory leak test and report results."
|
||||||
|
@ -434,6 +434,144 @@ def test_gr_rt7():
|
|||||||
check_routers(restarting="rt7")
|
check_routers(restarting="rt7")
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test rt1 performing an unplanned graceful restart
|
||||||
|
#
|
||||||
|
def test_unplanned_gr_rt1():
|
||||||
|
logger.info("Test: verify rt1 performing an unplanned graceful restart")
|
||||||
|
tgen = get_topogen()
|
||||||
|
|
||||||
|
# Skip if previous fatal error condition is raised
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
kill_router_daemons(tgen, "rt1", ["ospfd"], save_config=False)
|
||||||
|
start_router_daemons(tgen, "rt1", ["ospfd"])
|
||||||
|
|
||||||
|
expect_grace_lsa(restarting="1.1.1.1", area="0.0.0.1", helper="rt2")
|
||||||
|
ensure_gr_is_in_zebra("rt1")
|
||||||
|
check_routers(restarting="rt1")
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test rt2 performing an unplanned graceful restart
|
||||||
|
#
|
||||||
|
def test_unplanned_gr_rt2():
|
||||||
|
logger.info("Test: verify rt2 performing an unplanned graceful restart")
|
||||||
|
tgen = get_topogen()
|
||||||
|
|
||||||
|
# Skip if previous fatal error condition is raised
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
kill_router_daemons(tgen, "rt2", ["ospfd"], save_config=False)
|
||||||
|
start_router_daemons(tgen, "rt2", ["ospfd"])
|
||||||
|
|
||||||
|
expect_grace_lsa(restarting="2.2.2.2", area="0.0.0.1", helper="rt1")
|
||||||
|
expect_grace_lsa(restarting="2.2.2.2", area="0.0.0.0", helper="rt3")
|
||||||
|
ensure_gr_is_in_zebra("rt2")
|
||||||
|
check_routers(restarting="rt2")
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test rt3 performing an unplanned graceful restart
|
||||||
|
#
|
||||||
|
def test_unplanned_gr_rt3():
|
||||||
|
logger.info("Test: verify rt3 performing an unplanned graceful restart")
|
||||||
|
tgen = get_topogen()
|
||||||
|
|
||||||
|
# Skip if previous fatal error condition is raised
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
kill_router_daemons(tgen, "rt3", ["ospfd"], save_config=False)
|
||||||
|
start_router_daemons(tgen, "rt3", ["ospfd"])
|
||||||
|
|
||||||
|
expect_grace_lsa(restarting="3.3.3.3", area="0.0.0.0", helper="rt2")
|
||||||
|
expect_grace_lsa(restarting="3.3.3.3", area="0.0.0.0", helper="rt4")
|
||||||
|
expect_grace_lsa(restarting="3.3.3.3", area="0.0.0.0", helper="rt6")
|
||||||
|
ensure_gr_is_in_zebra("rt3")
|
||||||
|
check_routers(restarting="rt3")
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test rt4 performing an unplanned graceful restart
|
||||||
|
#
|
||||||
|
def test_unplanned_gr_rt4():
|
||||||
|
logger.info("Test: verify rt4 performing an unplanned graceful restart")
|
||||||
|
tgen = get_topogen()
|
||||||
|
|
||||||
|
# Skip if previous fatal error condition is raised
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
kill_router_daemons(tgen, "rt4", ["ospfd"], save_config=False)
|
||||||
|
start_router_daemons(tgen, "rt4", ["ospfd"])
|
||||||
|
|
||||||
|
expect_grace_lsa(restarting="4.4.4.4", area="0.0.0.0", helper="rt3")
|
||||||
|
expect_grace_lsa(restarting="4.4.4.4", area="0.0.0.2", helper="rt5")
|
||||||
|
ensure_gr_is_in_zebra("rt4")
|
||||||
|
check_routers(restarting="rt4")
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test rt5 performing an unplanned graceful restart
|
||||||
|
#
|
||||||
|
def test_unplanned_gr_rt5():
|
||||||
|
logger.info("Test: verify rt5 performing an unplanned graceful restart")
|
||||||
|
tgen = get_topogen()
|
||||||
|
|
||||||
|
# Skip if previous fatal error condition is raised
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
kill_router_daemons(tgen, "rt5", ["ospfd"], save_config=False)
|
||||||
|
start_router_daemons(tgen, "rt5", ["ospfd"])
|
||||||
|
|
||||||
|
expect_grace_lsa(restarting="5.5.5.5", area="0.0.0.2", helper="rt4")
|
||||||
|
ensure_gr_is_in_zebra("rt5")
|
||||||
|
check_routers(restarting="rt5")
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test rt6 performing an unplanned graceful restart
|
||||||
|
#
|
||||||
|
def test_unplanned_gr_rt6():
|
||||||
|
logger.info("Test: verify rt6 performing an unplanned graceful restart")
|
||||||
|
tgen = get_topogen()
|
||||||
|
|
||||||
|
# Skip if previous fatal error condition is raised
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
kill_router_daemons(tgen, "rt6", ["ospfd"], save_config=False)
|
||||||
|
start_router_daemons(tgen, "rt6", ["ospfd"])
|
||||||
|
|
||||||
|
expect_grace_lsa(restarting="6.6.6.6", area="0.0.0.0", helper="rt3")
|
||||||
|
expect_grace_lsa(restarting="6.6.6.6", area="0.0.0.3", helper="rt7")
|
||||||
|
ensure_gr_is_in_zebra("rt6")
|
||||||
|
check_routers(restarting="rt6")
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test rt7 performing an unplanned graceful restart
|
||||||
|
#
|
||||||
|
def test_unplanned_gr_rt7():
|
||||||
|
logger.info("Test: verify rt7 performing a graceful restart")
|
||||||
|
tgen = get_topogen()
|
||||||
|
|
||||||
|
# Skip if previous fatal error condition is raised
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
kill_router_daemons(tgen, "rt7", ["ospfd"], save_config=False)
|
||||||
|
start_router_daemons(tgen, "rt7", ["ospfd"])
|
||||||
|
|
||||||
|
expect_grace_lsa(restarting="7.7.7.7", area="0.0.0.3", helper="rt6")
|
||||||
|
ensure_gr_is_in_zebra("rt7")
|
||||||
|
check_routers(restarting="rt7")
|
||||||
|
|
||||||
|
|
||||||
# Memory leak test template
|
# Memory leak test template
|
||||||
def test_memory_leak():
|
def test_memory_leak():
|
||||||
"Run the memory leak test and report results."
|
"Run the memory leak test and report results."
|
||||||
|
Loading…
Reference in New Issue
Block a user