diff options
author | Acee <aceelindem@gmail.com> | 2023-05-18 16:43:52 +0200 |
---|---|---|
committer | Acee <aceelindem@gmail.com> | 2023-05-22 21:51:41 +0200 |
commit | 0d8ef0477ca02ead3ce9b6dd63f6943fd2b02dec (patch) | |
tree | 6695da96b114a226a7d63990f0e1d623b874fcce | |
parent | Merge pull request #13560 from donaldsharp/fpm_netlink_aroni (diff) | |
download | frr-0d8ef0477ca02ead3ce9b6dd63f6943fd2b02dec.tar.xz frr-0d8ef0477ca02ead3ce9b6dd63f6943fd2b02dec.zip |
ospfd: OSPF P2MP Delayed Reflooding configuration
Currently, delayed reflooding on P2MP interfaces for LSAs received
from neighbors on the interface is unconditionally (see commit
c706f0e32ba8aa8780a0618b6fbba364c383ae05). 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>
-rw-r--r-- | doc/user/ospfd.rst | 9 | ||||
-rw-r--r-- | lib/libospf.h | 1 | ||||
-rw-r--r-- | ospfd/ospf_flood.c | 29 | ||||
-rw-r--r-- | ospfd/ospf_interface.c | 1 | ||||
-rw-r--r-- | ospfd/ospf_interface.h | 6 | ||||
-rw-r--r-- | ospfd/ospf_vty.c | 53 | ||||
-rw-r--r-- | ospfd/ospfd.c | 1 | ||||
-rw-r--r-- | tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py | 179 |
8 files changed, 261 insertions, 18 deletions
diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index effad0fd0..3430d8a28 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -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) diff --git a/lib/libospf.h b/lib/libospf.h index 676b563ff..9eaca9a1a 100644 --- a/lib/libospf.h +++ b/lib/libospf.h @@ -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 diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c index f3fe504a0..a4d0f77fa 100644 --- a/ospfd/ospf_flood.c +++ b/ospfd/ospf_flood.c @@ -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; } diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index 8da982aed..2c66cb3cf 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -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; } diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h index 24768b9ab..ec1afa1b8 100644 --- a/ospfd/ospf_interface.h +++ b/ospfd/ospf_interface.h @@ -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; diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index f92be3fca..0ee42e0e7 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -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); diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index eae1f301a..51e937f42 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -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); diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py b/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py index 4f797743e..a90d7dbdc 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py @@ -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 |