ospfd: OSPF P2MP Delayed Reflooding configuration

Currently, delayed reflooding on P2MP interfaces for LSAs received
from neighbors on the interface is unconditionally (see commit
c706f0e32b). In some cases, this
change wasn't desirable and this feature makes delayed reflooding
configurable for P2MP interfaces via the CLI command:
"ip ospf network point-to-multipoint delay-reflood" in interface
submode.

Signed-off-by: Acee <aceelindem@gmail.com>
This commit is contained in:
Acee 2023-05-18 10:43:52 -04:00
parent 4d7f6295c6
commit 0d8ef0477c
8 changed files with 261 additions and 18 deletions

View File

@ -648,7 +648,7 @@ Interfaces
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 [delay-reflood]|point-to-point [dmvpn])
When configuring a point-to-point network on an interface and the interface
has a /32 address associated with then OSPF will treat the interface
@ -660,6 +660,13 @@ Interfaces
point-to-point, but the HUB will be a point-to-multipoint. To make this
topology work, specify the optional 'dmvpn' parameter at the spoke.
When the network is configured as point-to-multipoint and `delay-reflood`
is specified, LSAs received on the interface from neighbors on the
interface will not be flooded back out on the interface immediately.
Rather, they will be added to the neighbor's link state retransmission
list and only sent to the neighbor if the neighbor doesn't acknowledge
the LSA prior to the link state retransmission timer expiring.
Set explicitly network type for specified interface.
.. clicmd:: ip ospf priority (0-255)

View File

@ -68,6 +68,7 @@ extern "C" {
#define OSPF_MTU_IGNORE_DEFAULT 0
#define OSPF_FAST_HELLO_DEFAULT 0
#define OSPF_P2MP_DELAY_REFLOOD_DEFAULT false
#define OSPF_AREA_BACKBONE 0x00000000 /* 0.0.0.0 */
#define OSPF_AREA_RANGE_COST_UNSPEC -1U

View File

@ -770,15 +770,26 @@ int ospf_flood_through_interface(struct ospf_interface *oi,
OSPF_SEND_PACKET_DIRECT);
}
} else
/* Optimization: for P2MP interfaces,
don't send back out the incoming interface immediately,
allow time to rx multicast ack to the rx'ed (multicast)
update */
if (retx_flag != 1 ||
oi->type != OSPF_IFTYPE_POINTOMULTIPOINT || inbr == NULL ||
oi != inbr->oi)
ospf_ls_upd_send_lsa(oi->nbr_self, lsa,
OSPF_SEND_PACKET_INDIRECT);
/* If P2MP delayed reflooding is configured and the LSA was
received from a neighbor on the P2MP interface, do not flood
if back out on the interface. The LSA will be retransmitted
upon expiration of each neighbor's retransmission timer. This
will allow time to receive a multicast multicast link state
acknoweldgement and remove the LSA from each neighbor's link
state retransmission list. */
if (oi->p2mp_delay_reflood &&
(oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) &&
(inbr != NULL) && (oi == inbr->oi)) {
if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
zlog_debug(
"Delay reflooding for LSA[%s] from NBR %pI4 on interface %s",
dump_lsa_key(lsa),
inbr ? &(inbr->router_id)
: &(oi->ospf->router_id),
IF_NAME(oi));
} else
ospf_ls_upd_send_lsa(oi->nbr_self, lsa,
OSPF_SEND_PACKET_INDIRECT);
return 0;
}

View File

@ -545,6 +545,7 @@ static struct ospf_if_params *ospf_new_if_params(void)
oip->is_v_wait_set = false;
oip->ptp_dmvpn = 0;
oip->p2mp_delay_reflood = OSPF_P2MP_DELAY_REFLOOD_DEFAULT;
return oip;
}

View File

@ -109,6 +109,9 @@ struct ospf_if_params {
/* point-to-point DMVPN configuration */
uint8_t ptp_dmvpn;
/* point-to-multipoint delayed reflooding configuration */
bool p2mp_delay_reflood;
};
enum { MEMBER_ALLROUTERS = 0,
@ -177,6 +180,9 @@ struct ospf_interface {
/* point-to-point DMVPN configuration */
uint8_t ptp_dmvpn;
/* point-to-multipoint delayed reflooding */
bool p2mp_delay_reflood;
/* State of Interface State Machine. */
uint8_t state;

View File

@ -3958,6 +3958,16 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf,
/* OSPF Authentication information */
ospf_interface_auth_show(vty, oi, json_interface_sub, use_json);
if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) {
if (use_json)
json_object_boolean_add(json_interface_sub,
"p2mpDelayReflood",
oi->p2mp_delay_reflood);
else
vty_out(vty,
" %sDelay reflooding LSAs received on P2MP interface\n",
oi->p2mp_delay_reflood ? "" : "Don't ");
}
}
}
@ -8308,13 +8318,17 @@ DEFUN_HIDDEN (no_ospf_hello_interval,
}
DEFUN(ip_ospf_network, ip_ospf_network_cmd,
"ip ospf network <broadcast|non-broadcast|point-to-multipoint|point-to-point [dmvpn]>",
"ip ospf network <broadcast|"
"non-broadcast|"
"point-to-multipoint [delay-reflood]|"
"point-to-point [dmvpn]>",
"IP Information\n"
"OSPF interface commands\n"
"Network type\n"
"Specify OSPF broadcast multi-access network\n"
"Specify OSPF NBMA network\n"
"Specify OSPF point-to-multipoint network\n"
"Specify OSPF delayed reflooding of LSAs received on P2MP interface\n"
"Specify OSPF point-to-point network\n"
"Specify OSPF point-to-point DMVPN network\n")
{
@ -8322,6 +8336,7 @@ DEFUN(ip_ospf_network, ip_ospf_network_cmd,
int idx = 0;
int old_type = IF_DEF_PARAMS(ifp)->type;
uint8_t old_ptp_dmvpn = IF_DEF_PARAMS(ifp)->ptp_dmvpn;
uint8_t old_p2mp_delay_reflood = IF_DEF_PARAMS(ifp)->p2mp_delay_reflood;
struct route_node *rn;
if (old_type == OSPF_IFTYPE_LOOPBACK) {
@ -8331,21 +8346,26 @@ DEFUN(ip_ospf_network, ip_ospf_network_cmd,
}
IF_DEF_PARAMS(ifp)->ptp_dmvpn = 0;
IF_DEF_PARAMS(ifp)->p2mp_delay_reflood =
OSPF_P2MP_DELAY_REFLOOD_DEFAULT;
if (argv_find(argv, argc, "broadcast", &idx))
IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_BROADCAST;
else if (argv_find(argv, argc, "non-broadcast", &idx))
IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_NBMA;
else if (argv_find(argv, argc, "point-to-multipoint", &idx))
else if (argv_find(argv, argc, "point-to-multipoint", &idx)) {
IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_POINTOMULTIPOINT;
else if (argv_find(argv, argc, "point-to-point", &idx)) {
if (argv_find(argv, argc, "delay-reflood", &idx))
IF_DEF_PARAMS(ifp)->p2mp_delay_reflood = true;
} else if (argv_find(argv, argc, "point-to-point", &idx)) {
IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_POINTOPOINT;
if (argv_find(argv, argc, "dmvpn", &idx))
IF_DEF_PARAMS(ifp)->ptp_dmvpn = 1;
}
if (IF_DEF_PARAMS(ifp)->type == old_type
&& IF_DEF_PARAMS(ifp)->ptp_dmvpn == old_ptp_dmvpn)
if (IF_DEF_PARAMS(ifp)->type == old_type &&
IF_DEF_PARAMS(ifp)->ptp_dmvpn == old_ptp_dmvpn &&
IF_DEF_PARAMS(ifp)->p2mp_delay_reflood == old_p2mp_delay_reflood)
return CMD_SUCCESS;
SET_IF_PARAM(IF_DEF_PARAMS(ifp), type);
@ -8357,10 +8377,19 @@ DEFUN(ip_ospf_network, ip_ospf_network_cmd,
continue;
oi->type = IF_DEF_PARAMS(ifp)->type;
oi->ptp_dmvpn = IF_DEF_PARAMS(ifp)->ptp_dmvpn;
oi->p2mp_delay_reflood = IF_DEF_PARAMS(ifp)->p2mp_delay_reflood;
if (oi->state > ISM_Down) {
OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown);
OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceUp);
/*
* The OSPF interface only needs to be flapped if the network
* type or DMVPN parameter changes.
*/
if (IF_DEF_PARAMS(ifp)->type != old_type ||
IF_DEF_PARAMS(ifp)->ptp_dmvpn != old_ptp_dmvpn) {
if (oi->state > ISM_Down) {
OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown);
OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceUp);
}
}
}
@ -8398,6 +8427,8 @@ DEFUN (no_ip_ospf_network,
IF_DEF_PARAMS(ifp)->type = ospf_default_iftype(ifp);
IF_DEF_PARAMS(ifp)->ptp_dmvpn = 0;
IF_DEF_PARAMS(ifp)->p2mp_delay_reflood =
OSPF_P2MP_DELAY_REFLOOD_DEFAULT;
if (IF_DEF_PARAMS(ifp)->type == old_type)
return CMD_SUCCESS;
@ -8409,6 +8440,8 @@ DEFUN (no_ip_ospf_network,
continue;
oi->type = IF_DEF_PARAMS(ifp)->type;
oi->ptp_dmvpn = IF_DEF_PARAMS(ifp)->ptp_dmvpn;
oi->p2mp_delay_reflood = IF_DEF_PARAMS(ifp)->p2mp_delay_reflood;
if (oi->state > ISM_Down) {
OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown);
@ -11817,6 +11850,10 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf)
== OSPF_IFTYPE_POINTOPOINT
&& params->ptp_dmvpn)
vty_out(vty, " dmvpn");
if (params->type ==
OSPF_IFTYPE_POINTOMULTIPOINT &&
params->p2mp_delay_reflood)
vty_out(vty, " delay-reflood");
if (params != IF_DEF_PARAMS(ifp) && rn)
vty_out(vty, " %pI4",
&rn->p.u.prefix4);

View File

@ -1113,6 +1113,7 @@ struct ospf_interface *add_ospf_interface(struct connected *co,
skip network type setting. */
oi->type = IF_DEF_PARAMS(co->ifp)->type;
oi->ptp_dmvpn = IF_DEF_PARAMS(co->ifp)->ptp_dmvpn;
oi->p2mp_delay_reflood = IF_DEF_PARAMS(co->ifp)->p2mp_delay_reflood;
/* Add pseudo neighbor. */
ospf_nbr_self_reset(oi, oi->ospf->router_id);

View File

@ -363,6 +363,185 @@ def test_ospf_p2mp_tc1_p0(request):
write_test_footer(tc_name)
def test_ospf_p2mp_tc_delay_reflood(request):
"""OSPF IFSM -Verify "delay-reflood" parameter in p2mp network."""
tc_name = request.node.name
write_test_header(tc_name)
tgen = get_topogen()
r0 = tgen.gears["r0"]
# Don't run this test if we have any failure.
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
global topo
step("Verify for interface with network type P2MP that delay-reflood is configured")
r0.vtysh_multicmd(
"conf t\ninterface r0-r1-eth0\nip ospf network point-to-multipoint delay-reflood"
)
dut = "r0"
input_dict = {
"r0": {
"links": {
"r1": {
"ospf": {
"mcastMemberOspfAllRouters": True,
"ospfEnabled": True,
"networkType": "POINTOMULTIPOINT",
"p2mpDelayReflood": True,
}
},
"r2": {
"ospf": {
"mcastMemberOspfAllRouters": True,
"ospfEnabled": True,
"networkType": "POINTOMULTIPOINT",
"p2mpDelayReflood": False,
}
},
"r3": {
"ospf": {
"mcastMemberOspfAllRouters": True,
"ospfEnabled": True,
"networkType": "POINTOMULTIPOINT",
"p2mpDelayReflood": False,
}
},
}
}
}
result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
delay_reflood_cfg = (
tgen.net["r0"]
.cmd(
'vtysh -c "show running" | grep "^ ip ospf network point-to-multipoint delay-reflood"'
)
.rstrip()
)
assertmsg = "delay-reflood' configuration applied, but not present in configuration"
assert (
delay_reflood_cfg == " ip ospf network point-to-multipoint delay-reflood"
), assertmsg
step("Verify for interface with network type P2MP that delay-reflood is removed")
r0.vtysh_multicmd(
"conf t\ninterface r0-r1-eth0\nip ospf network point-to-multipoint"
)
input_dict = {
"r0": {
"links": {
"r1": {
"ospf": {
"mcastMemberOspfAllRouters": True,
"ospfEnabled": True,
"networkType": "POINTOMULTIPOINT",
"p2mpDelayReflood": False,
}
},
"r2": {
"ospf": {
"mcastMemberOspfAllRouters": True,
"ospfEnabled": True,
"networkType": "POINTOMULTIPOINT",
"p2mpDelayReflood": False,
}
},
"r3": {
"ospf": {
"mcastMemberOspfAllRouters": True,
"ospfEnabled": True,
"networkType": "POINTOMULTIPOINT",
"p2mpDelayReflood": False,
}
},
}
}
}
result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
delay_reflood_cfg = (
tgen.net["r0"]
.cmd(
'vtysh -c "show running" | grep "^ ip ospf network point-to-multipoint delay-reflood"'
)
.rstrip()
)
assertmsg = (
"delay-reflood' configuration removed, but still present in configuration"
)
assert (
delay_reflood_cfg != " ip ospf network point-to-multipoint delay-reflood"
), assertmsg
step(
"Verify for interface with network type P2MP that delay-reflood is removed with removal of network type"
)
r0.vtysh_multicmd(
"conf t\ninterface r0-r1-eth0\nip ospf network point-to-multipoint delay-reflood"
)
r0.vtysh_multicmd(
"conf t\ninterface r0-r1-eth0\nno ip ospf network point-to-multipoint"
)
r0.vtysh_multicmd(
"conf t\ninterface r0-r1-eth0\nip ospf network point-to-multipoint"
)
input_dict = {
"r0": {
"links": {
"r1": {
"ospf": {
"mcastMemberOspfAllRouters": True,
"ospfEnabled": True,
"networkType": "POINTOMULTIPOINT",
"p2mpDelayReflood": False,
}
},
"r2": {
"ospf": {
"mcastMemberOspfAllRouters": True,
"ospfEnabled": True,
"networkType": "POINTOMULTIPOINT",
"p2mpDelayReflood": False,
}
},
"r3": {
"ospf": {
"mcastMemberOspfAllRouters": True,
"ospfEnabled": True,
"networkType": "POINTOMULTIPOINT",
"p2mpDelayReflood": False,
}
},
}
}
}
result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
delay_reflood_cfg = (
tgen.net["r0"]
.cmd(
'vtysh -c "show running" | grep "^ ip ospf network point-to-multipoint delay-reflood"'
)
.rstrip()
)
assertmsg = (
"delay-reflood' configuration removed, but still present in configuration"
)
assert (
delay_reflood_cfg != " ip ospf network point-to-multipoint delay-reflood"
), assertmsg
write_test_footer(tc_name)
@retry(retry_timeout=30)
def verify_ospf_json(tgen, dut, input_dict, cmd="show ip ospf database json"):
del tgen