Skip to content

Commit

Permalink
*: Add and use option for graceful (re)start
Browse files Browse the repository at this point in the history
Add a new start option "-K" to libfrr to denote a graceful start,
and use it in zebra and bgpd.

zebra will use this option to denote a planned FRR graceful restart
(supporting only bgpd currently) to wait for a route sync completion
from bgpd before cleaning up old stale routes from the FIB. An optional
timer provides an upper-bounds for this cleanup.

bgpd will use this option to denote either a planned FRR graceful
restart or a bgpd-only graceful restart, and this will drive the BGP
GR restarting router procedures.

Signed-off-by: Vivek Venkatraman <[email protected]>
  • Loading branch information
vivek-cumulus authored and Pdoijode committed Jul 1, 2024
1 parent 425ea64 commit f7a73fa
Show file tree
Hide file tree
Showing 16 changed files with 221 additions and 92 deletions.
4 changes: 3 additions & 1 deletion bgpd/bgp_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,9 @@ int main(int argc, char **argv)
bgp_option_set(BGP_OPT_NO_FIB);
if (no_zebra_flag)
bgp_option_set(BGP_OPT_NO_ZEBRA);
SET_FLAG(bm->flags, BM_FLAG_GRACEFUL_RESTART);
if (bgpd_di.graceful_restart)
SET_FLAG(bm->flags, BM_FLAG_GRACEFUL_RESTART);

bgp_error_init();
/* Initializations. */
libagentx_init();
Expand Down
3 changes: 2 additions & 1 deletion bgpd/bgp_vty.c
Original file line number Diff line number Diff line change
Expand Up @@ -3706,9 +3706,10 @@ DEFPY (neighbor_graceful_shutdown,
afi_t afi;
safi_t safi;
struct peer *peer;
VTY_DECLVAR_CONTEXT(bgp, bgp);
int ret;

VTY_DECLVAR_CONTEXT(bgp, bgp);

peer = peer_and_group_lookup_vty(vty, neighbor);
if (!peer)
return CMD_WARNING_CONFIG_FAILED;
Expand Down
6 changes: 6 additions & 0 deletions doc/user/bgp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ be specified (:ref:`common-invocation-options`).
the operator has turned off communication to zebra and is running bgpd
as a complete standalone process.

.. option:: -K, --graceful_restart

Bgpd will use this option to denote either a planned FRR graceful
restart or a bgpd-only graceful restart, and this will drive the BGP
GR restarting router procedures.

LABEL MANAGER
-------------

Expand Down
45 changes: 28 additions & 17 deletions lib/libfrr.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,37 +102,41 @@ static void opt_extend(const struct optspec *os)
#define OPTION_SCRIPTDIR 1009

static const struct option lo_always[] = {
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'v'},
{"daemon", no_argument, NULL, 'd'},
{"module", no_argument, NULL, 'M'},
{"profile", required_argument, NULL, 'F'},
{"pathspace", required_argument, NULL, 'N'},
{"vrfdefaultname", required_argument, NULL, 'o'},
{"vty_socket", required_argument, NULL, OPTION_VTYSOCK},
{"moduledir", required_argument, NULL, OPTION_MODULEDIR},
{"scriptdir", required_argument, NULL, OPTION_SCRIPTDIR},
{"log", required_argument, NULL, OPTION_LOG},
{"log-level", required_argument, NULL, OPTION_LOGLEVEL},
{"command-log-always", no_argument, NULL, OPTION_LOGGING},
{"limit-fds", required_argument, NULL, OPTION_LIMIT_FDS},
{NULL}};
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'v' },
{ "daemon", no_argument, NULL, 'd' },
{ "module", no_argument, NULL, 'M' },
{ "profile", required_argument, NULL, 'F' },
{ "pathspace", required_argument, NULL, 'N' },
{ "vrfdefaultname", required_argument, NULL, 'o' },
{ "graceful_restart", optional_argument, NULL, 'K' },
{ "vty_socket", required_argument, NULL, OPTION_VTYSOCK },
{ "moduledir", required_argument, NULL, OPTION_MODULEDIR },
{ "scriptdir", required_argument, NULL, OPTION_SCRIPTDIR },
{ "log", required_argument, NULL, OPTION_LOG },
{ "log-level", required_argument, NULL, OPTION_LOGLEVEL },
{ "command-log-always", no_argument, NULL, OPTION_LOGGING },
{ "limit-fds", required_argument, NULL, OPTION_LIMIT_FDS },
{ NULL }
};
static const struct optspec os_always = {
"hvdM:F:N:o:",
"hvdM:F:N:o:K::",
" -h, --help Display this help and exit\n"
" -v, --version Print program version\n"
" -d, --daemon Runs in daemon mode\n"
" -M, --module Load specified module\n"
" -F, --profile Use specified configuration profile\n"
" -N, --pathspace Insert prefix into config & socket paths\n"
" -o, --vrfdefaultname Set default VRF name.\n"
" -K, --graceful_restart FRR starting in Graceful Restart mode, with optional route-cleanup timer\n"
" --vty_socket Override vty socket path\n"
" --moduledir Override modules directory\n"
" --scriptdir Override scripts directory\n"
" --log Set Logging to stdout, syslog, or file:<name>\n"
" --log-level Set Logging Level to use, debug, info, warn, etc\n"
" --limit-fds Limit number of fds supported\n",
lo_always};
lo_always
};

static bool logging_to_stdout = false; /* set when --log stdout specified */

Expand Down Expand Up @@ -358,6 +362,8 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv)
strlcpy(frr_protonameinst, di->logname, sizeof(frr_protonameinst));

di->cli_mode = FRR_CLI_CLASSIC;
di->graceful_restart = false;
di->gr_cleanup_time = 0;

/* we may be starting with extra FDs open for whatever purpose,
* e.g. logging, some module, etc. Recording them here allows later
Expand Down Expand Up @@ -520,6 +526,11 @@ static int frr_opt(int opt)
di->db_file = optarg;
break;
#endif
case 'K':
di->graceful_restart = true;
if (optarg)
di->gr_cleanup_time = atoi(optarg);
break;
case 'C':
if (di->flags & FRR_NO_SPLIT_CONFIG)
return 1;
Expand Down
2 changes: 2 additions & 0 deletions lib/libfrr.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ struct frr_daemon_info {
bool dryrun;
bool daemon_mode;
bool terminal;
bool graceful_restart;
int gr_cleanup_time;
enum frr_cli_mode cli_mode;

struct event *read_in;
Expand Down
55 changes: 28 additions & 27 deletions tests/topotests/lib/bgp.py
Original file line number Diff line number Diff line change
Expand Up @@ -3269,29 +3269,24 @@ def verify_graceful_restart(

# Local GR mode
if "bgp" not in input_dict[dut] and "graceful-restart" in input_dict[dut]:
if (
"graceful-restart" in input_dict[dut]["graceful-restart"]
and input_dict[dut]["graceful-restart"][
"graceful-restart"
]
):
lmode = "Restart*"
elif (
"graceful-restart-disable"
in input_dict[dut]["graceful-restart"]
and input_dict[dut]["graceful-restart"][
"graceful-restart-disable"
]
):
lmode = "Disable*"
else:
lmode = "Helper*"
if (
"graceful-restart" in input_dict[dut]["graceful-restart"]
and input_dict[dut]["graceful-restart"]["graceful-restart"]
):
lmode = "Restart*"
elif (
"graceful-restart-disable" in input_dict[dut]["graceful-restart"]
and input_dict[dut]["graceful-restart"]["graceful-restart-disable"]
):
lmode = "Disable*"
else:
lmode = "Helper*"

if lmode is None:
if "address_family" in input_dict[dut]["bgp"]:
bgp_neighbors = input_dict[dut]["bgp"]["address_family"][addr_type][
"unicast"
]["neighbor"][peer]["dest_link"]
"unicast"
]["neighbor"][peer]["dest_link"]

for dest_link, data in bgp_neighbors.items():
if (
Expand Down Expand Up @@ -3336,7 +3331,10 @@ def verify_graceful_restart(

# Remote GR mode

if "bgp" in input_dict[peer] and "address_family" in input_dict[peer]["bgp"]:
if (
"bgp" in input_dict[peer]
and "address_family" in input_dict[peer]["bgp"]
):
bgp_neighbors = input_dict[peer]["bgp"]["address_family"][addr_type][
"unicast"
]["neighbor"][dut]["dest_link"]
Expand All @@ -3358,7 +3356,10 @@ def verify_graceful_restart(
rmode = None

if rmode is None:
if "bgp" in input_dict[peer] and "graceful-restart" in input_dict[peer]["bgp"]:
if (
"bgp" in input_dict[peer]
and "graceful-restart" in input_dict[peer]["bgp"]
):
if (
"graceful-restart"
in input_dict[peer]["bgp"]["graceful-restart"]
Expand All @@ -3379,13 +3380,13 @@ def verify_graceful_restart(
rmode = "Helper"

if rmode is None:
if "bgp" not in input_dict[peer] and "graceful-restart" in input_dict[peer]:
if (
"bgp" not in input_dict[peer]
and "graceful-restart" in input_dict[peer]
):
if (
"graceful-restart"
in input_dict[peer]["graceful-restart"]
and input_dict[peer]["graceful-restart"][
"graceful-restart"
]
"graceful-restart" in input_dict[peer]["graceful-restart"]
and input_dict[peer]["graceful-restart"]["graceful-restart"]
):
rmode = "Restart"
elif (
Expand Down
33 changes: 19 additions & 14 deletions zebra/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,6 @@ struct mgmt_be_client *mgmt_be_client;
/* Route retain mode flag. */
int retain_mode = 0;

int graceful_restart;

/* Receive buffer size for kernel control sockets */
#define RCVBUFSIZE_MIN 4194304
#ifdef HAVE_NETLINK
Expand All @@ -88,15 +86,14 @@ const struct option longopts[] = {
{ "socket", required_argument, NULL, 'z' },
{ "ecmp", required_argument, NULL, 'e' },
{ "retain", no_argument, NULL, 'r' },
{ "graceful_restart", required_argument, NULL, 'K' },
{ "asic-offload", optional_argument, NULL, OPTION_ASIC_OFFLOAD },
{ "v6-with-v4-nexthops", no_argument, NULL, OPTION_V6_WITH_V4_NEXTHOP },
#ifdef HAVE_NETLINK
{ "vrfwnetns", no_argument, NULL, 'n' },
{ "nl-bufsize", required_argument, NULL, 's' },
{ "v6-rr-semantics", no_argument, NULL, OPTION_V6_RR_SEMANTICS },
#endif /* HAVE_NETLINK */
{"routing-table", optional_argument, NULL, 'R'},
{ "routing-table", optional_argument, NULL, 'R' },
{ 0 }
};

Expand Down Expand Up @@ -326,7 +323,6 @@ int main(int argc, char **argv)
bool v6_with_v4_nexthop = false;
bool notify_on_ack = true;

graceful_restart = 0;
vrf_configure_backend(VRF_BACKEND_VRF_LITE);

frr_preinit(&zebra_di, argc, argv);
Expand All @@ -342,7 +338,6 @@ int main(int argc, char **argv)
" -z, --socket Set path of zebra socket\n"
" -e, --ecmp Specify ECMP to use.\n"
" -r, --retain When program terminates, retain added route by zebra.\n"
" -K, --graceful_restart Graceful restart at the kernel level, timer in seconds for expiration\n"
" -A, --asic-offload FRR is interacting with an asic underneath the linux kernel\n"
" --v6-with-v4-nexthops Underlying dataplane supports v6 routes with v4 nexthops"
#ifdef HAVE_NETLINK
Expand All @@ -352,8 +347,7 @@ int main(int argc, char **argv)
#else
" -s, Set kernel socket receive buffer size\n"
#endif /* HAVE_NETLINK */
" -R, --routing-table Set kernel routing table\n"
);
" -R, --routing-table Set kernel routing table\n");

while (1) {
int opt = frr_getopt(argc, argv, NULL);
Expand Down Expand Up @@ -397,9 +391,6 @@ int main(int argc, char **argv)
case 'r':
retain_mode = 1;
break;
case 'K':
graceful_restart = atoi(optarg);
break;
case 's':
rcvbufsize = atoi(optarg);
if (rcvbufsize < RCVBUFSIZE_MIN)
Expand Down Expand Up @@ -488,11 +479,25 @@ int main(int argc, char **argv)
* Clean up zebra-originated routes. The requests will be sent to OS
* immediately, so originating PID in notifications from kernel
* will be equal to the current getpid(). To know about such routes,
* we have to have route_read() called before.
* we have to have route_read() called before.
* If FRR is gracefully restarting, we either wait for clients
* (e.g., BGP) to signal GR is complete else we wait for specified
* duration.
*/
zrouter.startup_time = monotime(NULL);
event_add_timer(zrouter.master, rib_sweep_route, NULL, graceful_restart,
&zrouter.sweeper);
zrouter.rib_sweep_time = 0;
zrouter.graceful_restart = zebra_di.graceful_restart;
if (!zrouter.graceful_restart)
event_add_timer(zrouter.master, rib_sweep_route, NULL, 0, NULL);
else {
int gr_cleanup_time;

gr_cleanup_time = zebra_di.gr_cleanup_time
? zebra_di.gr_cleanup_time
: ZEBRA_GR_DEFAULT_RIB_SWEEP_TIME;
event_add_timer(zrouter.master, rib_sweep_route, NULL,
gr_cleanup_time, &zrouter.t_rib_sweep);
}

/* Needed for BSD routing socket. */
pid = getpid();
Expand Down
4 changes: 2 additions & 2 deletions zebra/rib.h
Original file line number Diff line number Diff line change
Expand Up @@ -622,10 +622,10 @@ static inline struct nexthop_group *rib_get_fib_backup_nhg(
}

extern void zebra_gr_process_client(afi_t afi, vrf_id_t vrf_id, uint8_t proto,
uint8_t instance);
uint8_t instance, time_t restart_time);

extern int rib_add_gr_run(afi_t afi, vrf_id_t vrf_id, uint8_t proto,
uint8_t instance);
uint8_t instance, time_t restart_time);

extern void zebra_vty_init(void);

Expand Down
1 change: 1 addition & 0 deletions zebra/zapi_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -2414,6 +2414,7 @@ static void zsend_capabilities(struct zserv *client, struct zebra_vrf *zvrf)
stream_putl(s, zrouter.multipath_num);
stream_putc(s, zebra_mlag_get_role());
stream_putc(s, zrouter.v6_with_v4_nexthop);
stream_putc(s, zrouter.graceful_restart);
stream_putw_at(s, 0, stream_get_endp(s));
zserv_send_message(client, s);
}
Expand Down
Loading

0 comments on commit f7a73fa

Please sign in to comment.