bgpd: minimum-holdtime knob to prevent session establishment with BGP peer with low holdtime.

Signed-off-by: Takemasa Imada <takemasa.imada@gmail.com>
This commit is contained in:
Takemasa Imada 2021-08-14 14:32:40 +09:00
parent a01b086340
commit b042667a3d
11 changed files with 189 additions and 0 deletions

View File

@ -1353,6 +1353,16 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size)
return BGP_Stop;
}
/* Send notification message when Hold Time received in the OPEN message
* is smaller than configured minimum Hold Time. */
if (holdtime < peer->bgp->default_min_holdtime
&& peer->bgp->default_min_holdtime != 0) {
bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR,
BGP_NOTIFY_OPEN_UNACEP_HOLDTIME,
(uint8_t *)holdtime_ptr, 2);
return BGP_Stop;
}
/* From the rfc: A reasonable maximum time between KEEPALIVE messages
would be one third of the Hold Time interval. KEEPALIVE messages
MUST NOT be sent more frequently than one per second. An

View File

@ -2332,6 +2332,38 @@ DEFUN (no_bgp_timers,
return CMD_SUCCESS;
}
/* BGP minimum holdtime. */
DEFUN(bgp_minimum_holdtime, bgp_minimum_holdtime_cmd,
"bgp minimum-holdtime (1-65535)",
"BGP specific commands\n"
"BGP minimum holdtime\n"
"Seconds\n")
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
int idx_number = 2;
unsigned long min_holdtime;
min_holdtime = strtoul(argv[idx_number]->arg, NULL, 10);
bgp->default_min_holdtime = min_holdtime;
return CMD_SUCCESS;
}
DEFUN(no_bgp_minimum_holdtime, no_bgp_minimum_holdtime_cmd,
"no bgp minimum-holdtime [(1-65535)]",
NO_STR
"BGP specific commands\n"
"BGP minimum holdtime\n"
"Seconds\n")
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
bgp->default_min_holdtime = 0;
return CMD_SUCCESS;
}
DEFUN (bgp_client_to_client_reflection,
bgp_client_to_client_reflection_cmd,
@ -17112,6 +17144,12 @@ int bgp_config_write(struct vty *vty)
vty_out(vty, " timers bgp %u %u\n",
bgp->default_keepalive, bgp->default_holdtime);
/* BGP minimum holdtime configuration. */
if (bgp->default_min_holdtime != SAVE_BGP_HOLDTIME
&& bgp->default_min_holdtime != 0)
vty_out(vty, " bgp minimum-holdtime %u\n",
bgp->default_min_holdtime);
/* Conditional advertisement timer configuration */
if (bgp->condition_check_period
!= DEFAULT_CONDITIONAL_ROUTES_POLL_TIME)
@ -17506,6 +17544,10 @@ void bgp_vty_init(void)
install_element(BGP_NODE, &bgp_timers_cmd);
install_element(BGP_NODE, &no_bgp_timers_cmd);
/* "minimum-holdtime" commands. */
install_element(BGP_NODE, &bgp_minimum_holdtime_cmd);
install_element(BGP_NODE, &no_bgp_minimum_holdtime_cmd);
/* route-map delay-timer commands - per instance for backwards compat.
*/
install_element(BGP_NODE, &bgp_set_route_map_delay_timer_cmd);

View File

@ -3154,6 +3154,7 @@ static struct bgp *bgp_create(as_t *as, const char *name,
bgp->default_subgroup_pkt_queue_max =
BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX;
bgp_timers_unset(bgp);
bgp->default_min_holdtime = 0;
bgp->restart_time = BGP_DEFAULT_RESTART_TIME;
bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME;
bgp->select_defer_time = BGP_DEFAULT_SELECT_DEFERRAL_TIME;

View File

@ -602,6 +602,9 @@ struct bgp {
uint32_t default_connect_retry;
uint32_t default_delayopen;
/* BGP minimum holdtime. */
uint16_t default_min_holdtime;
/* BGP graceful restart */
uint32_t restart_time;
uint32_t stalepath_time;

View File

@ -1693,6 +1693,12 @@ Configuring Peers
default, the DelayOpenTimer is disabled. The timer interval may be set to a
duration of 1 to 240 seconds.
.. clicmd:: bgp minimum-holdtime (1-65535)
This command allows user to prevent session establishment with BGP peers
with lower holdtime less than configured minimum holdtime.
When this command is not set, minimum holdtime does not work.
Displaying Information about Peers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -0,0 +1,6 @@
router bgp 65000
bgp minimum-holdtime 20
neighbor 192.168.255.2 remote-as 65001
neighbor 192.168.255.2 timers 3 10
neighbor 192.168.255.2 timers connect 10
!

View File

@ -0,0 +1,6 @@
!
interface r1-eth0
ip address 192.168.255.1/24
!
ip forwarding
!

View File

@ -0,0 +1,5 @@
router bgp 65001
no bgp ebgp-requires-policy
neighbor 192.168.255.1 remote-as 65000
neighbor 192.168.255.1 timers 3 10
!

View File

@ -0,0 +1,6 @@
!
interface r2-eth0
ip address 192.168.255.2/24
!
ip forwarding
!

View File

@ -0,0 +1,104 @@
#!/usr/bin/env python
# Copyright (c) 2021 by
# Takemasa Imada <takemasa.imada@gmail.com>
#
# 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 NETDEF DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF 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.
#
"""
Test if minimum-holdtime works.
"""
import os
import sys
import json
import time
import pytest
import functools
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, "../"))
# pylint: disable=C0413
from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
pytestmark = [pytest.mark.bgpd]
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
tgen = get_topogen(self)
for routern in range(1, 3):
tgen.add_router("r{}".format(routern))
switch = tgen.add_switch("s1")
switch.add_link(tgen.gears["r1"])
switch.add_link(tgen.gears["r2"])
def setup_module(mod):
tgen = Topogen(TemplateTopo, mod.__name__)
tgen.start_topology()
router_list = tgen.routers()
for i, (rname, router) in enumerate(router_list.items(), 1):
router.load_config(
TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
)
router.load_config(
TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
)
tgen.start_router()
def teardown_module(mod):
tgen = get_topogen()
tgen.stop_topology()
def test_bgp_minimum_holdtime():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
def _bgp_neighbor_check_if_notification_sent():
output = json.loads(
tgen.gears["r1"].vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")
)
expected = {
"192.168.255.2": {
"connectionsEstablished": 0,
"lastNotificationReason": "OPEN Message Error/Unacceptable Hold Time",
"lastResetDueTo": "BGP Notification send",
}
}
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_neighbor_check_if_notification_sent)
success, result = topotest.run_and_expect(test_func, None, count=40, wait=0.5)
assert result is None, "Failed to send notification message\n"
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))