From f852eb98339fb3aacc52195bf5331c7b158557cc Mon Sep 17 00:00:00 2001 From: Prerana-GB Date: Fri, 6 Aug 2021 15:17:04 +0000 Subject: [PATCH 1/3] bgpd: BGP knob to teardown session immediately when peer is unreachable When BGP is notified by RIB that peer address is unreachable then BGP session must be brought down immediately and not wait for the hold-timer expiry. Today single-hop EBGP already behaves this way but need to change for iBGP and multi-hop EBGP sessions. Signed-off-by: Prerana G.B , Pushpasis Sarkar --- bgpd/bgp_fsm.c | 3 ++- bgpd/bgp_vty.c | 29 +++++++++++++++++++++++++++++ bgpd/bgpd.c | 2 +- bgpd/bgpd.h | 2 ++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index b62a42a4f6..0f2926d060 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -2196,7 +2196,8 @@ void bgp_fsm_nht_update(struct peer *peer, bool has_valid_nexthops) case OpenConfirm: case Established: if (!has_valid_nexthops - && (peer->gtsm_hops == BGP_GTSM_HOPS_CONNECTED)) + && (peer->gtsm_hops == BGP_GTSM_HOPS_CONNECTED + || peer->bgp->fast_convergence)) BGP_EVENT_ADD(peer, TCP_fatal_error); case Clearing: case Deleted: diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 63b2fbd4e6..ebd6e92d9b 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -4158,6 +4158,28 @@ DEFUN (neighbor_remote_as, return peer_remote_as_vty(vty, argv[idx_peer]->arg, argv[idx_remote_as]->arg); } +/* Enable fast convergence of bgp sessions. If this is enabled, bgp + * sessions do not wait for hold timer expiry to bring down the sessions + * when nexthop becomes unreachable + */ +DEFUN(bgp_fast_convergence, bgp_fast_convergence_cmd, "bgp fast-convergence", + BGP_STR "Fast convergence for bgp sessions\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + bgp->fast_convergence = true; + + return CMD_SUCCESS; +} + +DEFUN(no_bgp_fast_convergence, no_bgp_fast_convergence_cmd, + "no bgp fast-convergence", + NO_STR BGP_STR "Fast convergence for bgp sessions\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + bgp->fast_convergence = false; + + return CMD_SUCCESS; +} static int peer_conf_interface_get(struct vty *vty, const char *conf_if, int v6only, @@ -17147,6 +17169,9 @@ int bgp_config_write(struct vty *vty) if (CHECK_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN)) vty_out(vty, " bgp shutdown\n"); + if (bgp->fast_convergence) + vty_out(vty, " bgp fast-convergence\n"); + if (bgp->srv6_enabled) { vty_frame(vty, " !\n segment-routing srv6\n"); if (strlen(bgp->srv6_locator_name)) @@ -17410,6 +17435,10 @@ void bgp_vty_init(void) install_element(CONFIG_NODE, &bgp_set_route_map_delay_timer_cmd); install_element(CONFIG_NODE, &no_bgp_set_route_map_delay_timer_cmd); + /* bgp fast-convergence command */ + install_element(BGP_NODE, &bgp_fast_convergence_cmd); + install_element(BGP_NODE, &no_bgp_fast_convergence_cmd); + /* global bgp update-delay command */ install_element(CONFIG_NODE, &bgp_global_update_delay_cmd); install_element(CONFIG_NODE, &no_bgp_global_update_delay_cmd); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 591fc1214c..1c558cc550 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -3165,7 +3165,7 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp->reject_as_sets = false; bgp->condition_check_period = DEFAULT_CONDITIONAL_ROUTES_POLL_TIME; bgp_addpath_init_bgp_data(&bgp->tx_addpath); - + bgp->fast_convergence = false; bgp->as = *as; #ifdef ENABLE_BGP_VNC diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 867062a88b..ed7642f315 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -746,6 +746,8 @@ struct bgp { /* Process Queue for handling routes */ struct work_queue *process_queue; + bool fast_convergence; + /* BGP Conditional advertisement */ uint32_t condition_check_period; uint32_t condition_filter_count; From 57ea19204c1c463a88fe62cadaeab20d58f13c7d Mon Sep 17 00:00:00 2001 From: Prerana GB Date: Fri, 6 Aug 2021 18:35:19 +0000 Subject: [PATCH 2/3] topotests: Topotest changes for new bgp fast convergence knob Signed-off-by: Kuldeep Kashyap --- .../bgp_ecmp_topo3/ibgp_ecmp_topo3.json | 232 +++++++++++++ .../bgp_ecmp_topo3/test_ibgp_ecmp_topo3.py | 310 ++++++++++++++++++ 2 files changed, 542 insertions(+) create mode 100644 tests/topotests/bgp_ecmp_topo3/ibgp_ecmp_topo3.json create mode 100644 tests/topotests/bgp_ecmp_topo3/test_ibgp_ecmp_topo3.py diff --git a/tests/topotests/bgp_ecmp_topo3/ibgp_ecmp_topo3.json b/tests/topotests/bgp_ecmp_topo3/ibgp_ecmp_topo3.json new file mode 100644 index 0000000000..b01f9023b0 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo3/ibgp_ecmp_topo3.json @@ -0,0 +1,232 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link2": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 60, + "holddowntimer": 180, + "next_hop_self": true + }, + "r2-link2": { + "keepalivetimer": 60, + "holddowntimer": 180, + "next_hop_self": true + } + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 60, + "holddowntimer": 180, + "next_hop_self": true + }, + "r2-link2": { + "keepalivetimer": 60, + "holddowntimer": 180, + "next_hop_self": true + } + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link2": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "maximum_paths": { + "ibgp": 2 + }, + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 60, + "holddowntimer": 180 + }, + "r3-link2": { + "keepalivetimer": 60, + "holddowntimer": 180 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "maximum_paths": { + "ibgp": 2 + }, + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 60, + "holddowntimer": 180, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link2": { + "keepalivetimer": 60, + "holddowntimer": 180, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_ecmp_topo3/test_ibgp_ecmp_topo3.py b/tests/topotests/bgp_ecmp_topo3/test_ibgp_ecmp_topo3.py new file mode 100644 index 0000000000..5f3ac4e716 --- /dev/null +++ b/tests/topotests/bgp_ecmp_topo3/test_ibgp_ecmp_topo3.py @@ -0,0 +1,310 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +""" +Following tests are covered to test ecmp functionality on iBGP. +1. Verify bgp fast-convergence functionality +""" +import os +import sys +import time +import json +import pytest +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + verify_rib, + create_static_routes, + check_address_types, + interface_status, + reset_config_on_routers, + required_linux_kernel_version, + shutdown_bringup_interface, + apply_raw_config, +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp +from lib.topojson import build_topo_from_json, build_config_from_json + + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + + +# Reading the data from JSON File for topology and configuration creation +jsonFile = "{}/ibgp_ecmp_topo3.json".format(CWD) + +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +NEXT_HOPS = {"ipv4": [], "ipv6": []} +NETWORK = {"ipv4": "192.168.1.10/32", "ipv6": "fd00:0:0:1::10/128"} +NEXT_HOP_IP = {"ipv4": "10.0.0.1", "ipv6": "fd00::1"} +BGP_CONVERGENCE = False + + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment. + + * `mod`: module name + """ + global NEXT_HOPS, INTF_LIST_R3, INTF_LIST_R2, TEST_STATIC + global ADDR_TYPES + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + # STATIC_ROUTE = True + logger.info("Running setup_module() done") + + +def teardown_module(): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def static_or_nw(tgen, topo, tc_name, test_type, dut): + + if test_type == "redist_static": + input_dict_static = { + dut: { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": NEXT_HOP_IP["ipv4"]}, + {"network": NETWORK["ipv6"], "next_hop": NEXT_HOP_IP["ipv6"]}, + ] + } + } + logger.info("Configuring static route on router %s", dut) + result = create_static_routes(tgen, input_dict_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + dut: { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + } + } + } + } + + logger.info("Configuring redistribute static route on router %s", dut) + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + elif test_type == "advertise_nw": + input_dict_nw = { + dut: { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": NETWORK["ipv4"]}] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [{"network": NETWORK["ipv6"]}] + } + }, + } + } + } + } + + logger.info( + "Advertising networks %s %s from router %s", + NETWORK["ipv4"], + NETWORK["ipv6"], + dut, + ) + result = create_router_bgp(tgen, topo, input_dict_nw) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + +@pytest.mark.parametrize("test_type", ["redist_static"]) +def test_ecmp_fast_convergence(request, test_type): + """This test is to verify bgp fast-convergence cli functionality""" + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + reset_config_on_routers(tgen) + static_or_nw(tgen, topo, tc_name, test_type, "r2") + + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + intf1 = topo["routers"]["r2"]["links"]["r3-link1"]["interface"] + intf2 = topo["routers"]["r2"]["links"]["r3-link2"]["interface"] + + logger.info("Shutdown one of the link b/w r2 and r3") + shutdown_bringup_interface(tgen, "r2", intf1, False) + + logger.info("Verify bgp neighbors are still up") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + logger.info("Shutdown another link b/w r2 and r3") + shutdown_bringup_interface(tgen, "r2", intf2, False) + + logger.info("Wait for 10 sec and make sure bgp neighbors are still up") + sleep(10) + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + logger.info("No shut links b/w r2 and r3") + shutdown_bringup_interface(tgen, "r2", intf1, True) + shutdown_bringup_interface(tgen, "r2", intf2, True) + + logger.info("Enable bgp fast-convergence cli") + raw_config = { + "r2": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r2"]["bgp"]["local_as"]), + "bgp fast-convergence", + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + logger.info("Shutdown one link b/w r2 and r3") + shutdown_bringup_interface(tgen, "r2", intf1, False) + + logger.info("Verify bgp neighbors goes down immediately") + result = verify_bgp_convergence(tgen, topo, dut="r2", expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + logger.info("Shutdown second link b/w r2 and r3") + shutdown_bringup_interface(tgen, "r2", intf2, False) + + logger.info("Verify bgp neighbors goes down immediately") + result = verify_bgp_convergence(tgen, topo, dut="r2", expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) From 5874235fb9e5b5c4c57e1aa4b0db4b310d6e3b23 Mon Sep 17 00:00:00 2001 From: Prerana GB Date: Fri, 6 Aug 2021 18:41:32 +0000 Subject: [PATCH 3/3] doc: Updating the BGP document with BGP fast convergence CLI Signed-off-by: Prerana G.B --- doc/user/bgp.rst | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 5df149a174..6586b42722 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -4339,3 +4339,35 @@ Show command json output: .. [bgp-route-osci-cond] McPherson, D. and Gill, V. and Walton, D., "Border Gateway Protocol (BGP) Persistent Route Oscillation Condition", IETF RFC3345 .. [stable-flexible-ibgp] Flavel, A. and M. Roughan, "Stable and flexible iBGP", ACM SIGCOMM 2009 .. [ibgp-correctness] Griffin, T. and G. Wilfong, "On the correctness of IBGP configuration", ACM SIGCOMM 2002 + +.. _bgp-fast-convergence: + +BGP fast-convergence support +============================ +Whenever BGP peer address becomes unreachable we must bring down the BGP +session immediately. Currently only single-hop EBGP sessions are brought +down immediately.IBGP and multi-hop EBGP sessions wait for hold-timer +expiry to bring down the sessions. + +This new configuration option helps user to teardown BGP sessions immediately +whenever peer becomes unreachable. + +.. clicmd:: bgp fast-convergence + +This configuration is available at the bgp level. When enabled, configuration +is applied to all the neighbors configured in that bgp instance. + +.. code-block:: frr + + router bgp 64496 + neighbor 10.0.0.2 remote-as 64496 + neighbor fd00::2 remote-as 64496 + bgp fast-convergence + ! + address-family ipv4 unicast + redistribute static + exit-address-family + ! + address-family ipv6 unicast + neighbor fd00::2 activate + exit-address-family