Merge pull request #15660 from LabNConsulting/acee/ospf-p2mp-non-broadcast

ospfd: Implement non-broadcast support for point-to-multipoint networks
This commit is contained in:
Russ White 2024-04-09 11:56:34 -04:00 committed by GitHub
commit 7f6cda36b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 1189 additions and 201 deletions

View File

@ -494,11 +494,11 @@ Graceful Restart
Configure Graceful Restart (RFC 5187) helper support.
By default, helper support is disabled for all neighbours.
By default, helper support is disabled for all neighbors.
This config enables/disables helper support on this router
for all neighbours.
for all neighbors.
To enable/disable helper support for a specific
neighbour, the router-id (A.B.C.D) has to be specified.
neighbor, the router-id (A.B.C.D) has to be specified.
.. clicmd:: graceful-restart helper strict-lsa-checking

View File

@ -12,7 +12,7 @@ OSPF Fundamentals
:term:`distance-vector` protocols, such as :abbr:`RIP` or :abbr:`BGP`, where
routers describe available `paths` (i.e. routes) to each other, in
:term:`link-state` protocols routers instead describe the state of their links
to their immediate neighbouring routers.
to their immediate neighboring routers.
.. index::
single: Link State Announcement
@ -127,7 +127,7 @@ LSA Flooding
""""""""""""
OSPF defines several related mechanisms, used to manage synchronisation of
:abbr:`LSDB` s between neighbours as neighbours form adjacencies and the
:abbr:`LSDB` s between neighbors as neighbors form adjacencies and the
propagation, or `flooding` of new or updated :abbr:`LSA` s.
@ -259,7 +259,7 @@ called `intra-area routes`.
LSA is originated for such a link.
Stub
A link with no adjacent neighbours, or a host route.
A link with no adjacent neighbors, or a host route.
- Link ID and Data
@ -339,8 +339,8 @@ The example below shows two :abbr:`LSA` s, both originated by the same router
of different LSA types.
The first LSA being the router LSA describing 192.168.0.49's links: 2 links
to multi-access networks with fully-adjacent neighbours (i.e. Transit
links) and 1 being a Stub link (no adjacent neighbours).
to multi-access networks with fully-adjacent neighbors (i.e. Transit
links) and 1 being a Stub link (no adjacent neighbors).
The second LSA being a Network LSA, for which 192.168.0.49 is the
:abbr:`DR`, listing the Router IDs of 4 routers on that network which

View File

@ -239,6 +239,17 @@ To start OSPF process you have to specify the OSPF router.
This configuration setting MUST be consistent across all routers within the
OSPF domain.
.. clicmd:: neighbor A.B.C.D [poll-interval (1-65535)] [priority (0-255)]
Configures OSPF neighbors for non-broadcast multi-access (NBMA) networks
and point-to-multipoint non-broadcast networks. The `poll-interval`
specifies the rate for sending hello packets to neighbors that are not
active. When the configured neighbor is discovered, hello packets will be
sent at the rate of the hello-interval. The default `poll-interval` is 60
seconds. The `priority` is used to for the Designated Router (DR) election
on non-broadcast multi-access networks.
.. clicmd:: network A.B.C.D/M area A.B.C.D
.. clicmd:: network A.B.C.D/M area (0-4294967295)
@ -580,7 +591,7 @@ Interfaces
Note that OSPF MD5 authentication requires that time never go backwards
(correct time is NOT important, only that it never goes backwards), even
across resets, if ospfd is to be able to promptly reestablish adjacencies
with its neighbours after restarts/reboots. The host should have system time
with its neighbors after restarts/reboots. The host should have system time
be set at boot from an external or non-volatile source (e.g. battery backed
clock, NTP, etc.) or else the system clock should be periodically saved to
non-volatile storage and restored at boot if MD5 authentication is to be
@ -612,7 +623,7 @@ Interfaces
Note that OSPF HMAC cryptographic authentication requires that time never go backwards
(correct time is NOT important, only that it never goes backwards), even
across resets, if ospfd is to be able to promptly reestablish adjacencies
with its neighbours after restarts/reboots. The host should have system time
with its neighbors after restarts/reboots. The host should have system time
be set at boot from an external or non-volatile source (e.g. battery backed
clock, NTP, etc.) or else the system clock should be periodically saved to
non-volatile storage and restored at boot if HMAC cryptographic authentication is to be
@ -679,7 +690,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 [delay-reflood]|point-to-point [dmvpn])
.. clicmd:: ip ospf network (broadcast|non-broadcast|point-to-multipoint [delay-reflood|non-broadcast]|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
@ -691,6 +702,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 `non-broadcast`
is specified, the network doesn't support broadcast or multicast delivery
and neighbors cannot be discovered from OSPF hello received from the
OSPFAllRouters (224.0.0.5). Rather, they must be explicitly configured
using the :clicmd:`neighbor A.B.C.D` configuration command as they are
on non-broadcast networks.
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.
@ -838,11 +856,11 @@ Graceful Restart
Configure Graceful Restart (RFC 3623) helper support.
By default, helper support is disabled for all neighbours.
By default, helper support is disabled for all neighbors.
This config enables/disables helper support on this router
for all neighbours.
for all neighbors.
To enable/disable helper support for a specific
neighbour, the router-id (A.B.C.D) has to be specified.
neighbor, the router-id (A.B.C.D) has to be specified.
.. clicmd:: graceful-restart helper strict-lsa-checking
@ -1082,7 +1100,7 @@ Router Information
respectively the PCE IP address, Autonomous System (AS) numbers of
controlled domains, neighbor ASs, flag and scope. For flag and scope, please
refer to :rfc`5088` for the BITPATTERN recognition. Multiple 'pce neighbor'
command could be specified in order to specify all PCE neighbours.
command could be specified in order to specify all PCE neighbors.
.. clicmd:: show ip ospf router-info

View File

@ -69,6 +69,7 @@ extern "C" {
#define OSPF_MTU_IGNORE_DEFAULT 0
#define OSPF_FAST_HELLO_DEFAULT 0
#define OSPF_P2MP_DELAY_REFLOOD_DEFAULT false
#define OSPF_P2MP_NON_BROADCAST_DEFAULT false
#define OSPF_OPAQUE_CAPABLE_DEFAULT true
#define OSPF_PREFIX_SUPPRESSION_DEFAULT false
#define OSPF_AREA_BACKBONE 0x00000000 /* 0.0.0.0 */

View File

@ -765,8 +765,9 @@ int ospf_flood_through_interface(struct ospf_interface *oi,
packets must be sent, as unicasts, to each adjacent neighbor
(i.e., those in state Exchange or greater). The destination
IP addresses for these packets are the neighbors' IP
addresses. */
if (oi->type == OSPF_IFTYPE_NBMA) {
addresses. This behavior is extended to P2MP networks which
don't support broadcast. */
if (OSPF_IF_NON_BROADCAST(oi)) {
struct ospf_neighbor *nbr;
for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) {

View File

@ -147,17 +147,11 @@ void ospf_if_reset(struct interface *ifp)
}
}
void ospf_if_reset_variables(struct ospf_interface *oi)
static void ospf_if_default_variables(struct ospf_interface *oi)
{
/* Set default values. */
/* don't clear this flag. oi->flag = OSPF_IF_DISABLE; */
if (oi->vl_data)
oi->type = OSPF_IFTYPE_VIRTUALLINK;
else
/* preserve network-type */
if (oi->type != OSPF_IFTYPE_NBMA)
oi->type = OSPF_IFTYPE_BROADCAST;
oi->type = OSPF_IFTYPE_BROADCAST;
oi->state = ISM_Down;
@ -254,7 +248,7 @@ struct ospf_interface *ospf_if_new(struct ospf *ospf, struct interface *ifp,
oi->ls_ack_direct.ls_ack = list_new();
/* Set default values. */
ospf_if_reset_variables(oi);
ospf_if_default_variables(oi);
/* Set pseudo neighbor to Null */
oi->nbr_self = NULL;

View File

@ -119,6 +119,9 @@ struct ospf_if_params {
/* point-to-multipoint delayed reflooding configuration */
bool p2mp_delay_reflood;
/* point-to-multipoint doesn't support broadcast */
bool p2mp_non_broadcast;
/* Opaque LSA capability at interface level (see RFC5250) */
DECLARE_IF_PARAM(bool, opaque_capable);
};
@ -185,6 +188,10 @@ struct ospf_interface {
/* OSPF Network Type. */
uint8_t type;
#define OSPF_IF_NON_BROADCAST(O) \
(((O)->type == OSPF_IFTYPE_NBMA) || \
((((O)->type == OSPF_IFTYPE_POINTOMULTIPOINT) && \
(O)->p2mp_non_broadcast)))
/* point-to-point DMVPN configuration */
uint8_t ptp_dmvpn;
@ -192,6 +199,9 @@ struct ospf_interface {
/* point-to-multipoint delayed reflooding */
bool p2mp_delay_reflood;
/* point-to-multipoint doesn't support broadcast */
bool p2mp_non_broadcast;
/* State of Interface State Machine. */
uint8_t state;
@ -326,7 +336,6 @@ extern void ospf_if_update_params(struct interface *ifp, struct in_addr addr);
extern int ospf_if_new_hook(struct interface *ifp);
extern void ospf_if_init(void);
extern void ospf_if_stream_unset(struct ospf_interface *oi);
extern void ospf_if_reset_variables(struct ospf_interface *oi);
extern int ospf_if_is_enable(struct ospf_interface *oi);
extern int ospf_if_get_output_cost(struct ospf_interface *oi);
extern void ospf_if_recalculate_output_cost(struct interface *ifp);

View File

@ -367,7 +367,7 @@ static int ism_interface_up(struct ospf_interface *oi)
/* Otherwise, the state transitions to Waiting. */
next_state = ISM_Waiting;
if (oi->type == OSPF_IFTYPE_NBMA)
if (OSPF_IF_NON_BROADCAST(oi))
ospf_nbr_nbma_if_update(oi->ospf, oi);
/* ospf_ism_event (t); */

View File

@ -431,7 +431,7 @@ static struct ospf_neighbor *ospf_nbr_add(struct ospf_interface *oi,
memcpy(&nbr->address, p, sizeof(struct prefix));
nbr->nbr_nbma = NULL;
if (oi->type == OSPF_IFTYPE_NBMA) {
if (OSPF_IF_NON_BROADCAST(oi)) {
struct ospf_nbr_nbma *nbr_nbma;
struct listnode *node;
@ -485,7 +485,7 @@ struct ospf_neighbor *ospf_nbr_get(struct ospf_interface *oi,
route_unlock_node(rn);
nbr = rn->info;
if (oi->type == OSPF_IFTYPE_NBMA && nbr->state == NSM_Attempt) {
if (OSPF_IF_NON_BROADCAST(nbr->oi) && nbr->state == NSM_Attempt) {
nbr->src = iph->ip_src;
memcpy(&nbr->address, p, sizeof(struct prefix));
}

View File

@ -166,7 +166,7 @@ static int nsm_hello_received(struct ospf_neighbor *nbr)
OSPF_NSM_TIMER_ON(nbr->t_inactivity, ospf_inactivity_timer,
nbr->v_inactivity);
if (nbr->oi->type == OSPF_IFTYPE_NBMA && nbr->nbr_nbma)
if (OSPF_IF_NON_BROADCAST(nbr->oi) && nbr->nbr_nbma != NULL)
EVENT_OFF(nbr->nbr_nbma->t_poll);
/* Send proactive ARP requests */
@ -377,7 +377,7 @@ static int nsm_kill_nbr(struct ospf_neighbor *nbr)
return 0;
}
if (nbr->oi->type == OSPF_IFTYPE_NBMA && nbr->nbr_nbma != NULL) {
if (OSPF_IF_NON_BROADCAST(nbr->oi) && nbr->nbr_nbma != NULL) {
struct ospf_nbr_nbma *nbr_nbma = nbr->nbr_nbma;
nbr_nbma->nbr = NULL;

View File

@ -3395,17 +3395,19 @@ static void ospf_poll_send(struct ospf_nbr_nbma *nbr_nbma)
if (OSPF_IF_PASSIVE_STATUS(oi) == OSPF_IF_PASSIVE)
return;
if (oi->type != OSPF_IFTYPE_NBMA)
return;
if (nbr_nbma->nbr != NULL && nbr_nbma->nbr->state != NSM_Down)
return;
if (PRIORITY(oi) == 0)
return;
if (oi->type == OSPF_IFTYPE_NBMA) {
if (PRIORITY(oi) == 0)
return;
if (nbr_nbma->priority == 0 && oi->state != ISM_DR
&& oi->state != ISM_Backup)
if (nbr_nbma->priority == 0 && oi->state != ISM_DR &&
oi->state != ISM_Backup)
return;
} else if (oi->type != OSPF_IFTYPE_POINTOMULTIPOINT ||
!oi->p2mp_non_broadcast)
return;
ospf_hello_send_sub(oi, nbr_nbma->addr.s_addr);
@ -3451,7 +3453,7 @@ void ospf_hello_send(struct ospf_interface *oi)
if (OSPF_IF_PASSIVE_STATUS(oi) == OSPF_IF_PASSIVE)
return;
if (oi->type == OSPF_IFTYPE_NBMA) {
if (OSPF_IF_NON_BROADCAST(oi)) {
struct ospf_neighbor *nbr;
struct route_node *rn;
@ -3467,31 +3469,44 @@ void ospf_hello_send(struct ospf_interface *oi)
continue;
/*
* RFC 2328 Section 9.5.1
* If the router is not eligible to become Designated
* Router, it must periodically send Hello Packets to
* both the Designated Router and the Backup
* Designated Router (if they exist).
* Always send to all neighbors on Point-to-Multipoint
* non-braodcast networks.
*/
if (PRIORITY(oi) == 0 &&
IPV4_ADDR_CMP(&DR(oi), &nbr->address.u.prefix4) &&
IPV4_ADDR_CMP(&BDR(oi), &nbr->address.u.prefix4))
continue;
if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT)
ospf_hello_send_sub(oi, nbr->address.u.prefix4
.s_addr);
else {
/*
* RFC 2328 Section 9.5.1
* If the router is not eligible to become Designated
* Router, it must periodically send Hello Packets to
* both the Designated Router and the Backup
* Designated Router (if they exist).
*/
if (PRIORITY(oi) == 0 &&
IPV4_ADDR_CMP(&DR(oi),
&nbr->address.u.prefix4) &&
IPV4_ADDR_CMP(&BDR(oi),
&nbr->address.u.prefix4))
continue;
/*
* If the router is eligible to become Designated
* Router, it must periodically send Hello Packets to
* all neighbors that are also eligible. In addition,
* if the router is itself the Designated Router or
* Backup Designated Router, it must also send periodic
* Hello Packets to all other neighbors.
*/
if (nbr->priority == 0 && oi->state == ISM_DROther)
continue;
/*
* If the router is eligible to become Designated
* Router, it must periodically send Hello Packets to
* all neighbors that are also eligible. In addition,
* if the router is itself the Designated Router or
* Backup Designated Router, it must also send periodic
* Hello Packets to all other neighbors.
*/
if (nbr->priority == 0 &&
oi->state == ISM_DROther)
continue;
/* if oi->state == Waiting, send
* hello to all neighbors */
ospf_hello_send_sub(oi, nbr->address.u.prefix4.s_addr);
/* if oi->state == Waiting, send
* hello to all neighbors */
ospf_hello_send_sub(oi, nbr->address.u.prefix4
.s_addr);
}
}
} else {
/* Decide destination address. */
@ -3848,11 +3863,10 @@ void ospf_ls_upd_send(struct ospf_neighbor *nbr, struct list *update, int flag,
else
p.prefix.s_addr = htonl(OSPF_ALLDROUTERS);
if (oi->type == OSPF_IFTYPE_NBMA) {
if (OSPF_IF_NON_BROADCAST(oi)) {
if (flag == OSPF_SEND_PACKET_INDIRECT)
flog_warn(
EC_OSPF_PACKET,
"* LS-Update is directly sent on NBMA network.");
flog_warn(EC_OSPF_PACKET,
"* LS-Update is directly sent on non-broadcast network.");
if (IPV4_ADDR_SAME(&oi->address->u.prefix4, &p.prefix))
flog_warn(EC_OSPF_PACKET,
"* LS-Update is sent to myself.");
@ -3910,7 +3924,8 @@ static void ospf_ls_ack_send_list(struct ospf_interface *oi, struct list *ack,
/* Decide destination address. */
if (oi->type == OSPF_IFTYPE_POINTOPOINT ||
oi->type == OSPF_IFTYPE_POINTOMULTIPOINT)
(oi->type == OSPF_IFTYPE_POINTOMULTIPOINT &&
!oi->p2mp_non_broadcast))
op->dst.s_addr = htonl(OSPF_ALLSPFROUTERS);
else
op->dst.s_addr = dst.s_addr;
@ -3962,7 +3977,7 @@ void ospf_ls_ack_send_delayed(struct ospf_interface *oi)
networks, delayed Link State Acknowledgment packets must be
unicast separately over each adjacency (i.e., neighbor whose
state is >= Exchange). */
if (oi->type == OSPF_IFTYPE_NBMA) {
if (OSPF_IF_NON_BROADCAST(oi)) {
struct ospf_neighbor *nbr;
struct route_node *rn;

View File

@ -2406,130 +2406,30 @@ DEFUN (no_ospf_timers_lsa_min_arrival,
return CMD_SUCCESS;
}
DEFUN (ospf_neighbor,
ospf_neighbor_cmd,
"neighbor A.B.C.D [priority (0-255) [poll-interval (1-65535)]]",
NEIGHBOR_STR
"Neighbor IP address\n"
"Neighbor Priority\n"
"Priority\n"
"Dead Neighbor Polling interval\n"
"Seconds\n")
DEFPY(ospf_neighbor, ospf_neighbor_cmd,
"[no] neighbor A.B.C.D$nbr_address [{priority (0-255)$priority | poll-interval (1-65535)$interval}]",
NO_STR
NEIGHBOR_STR
"Neighbor IP address\n"
"Neighbor Priority\n"
"Priority\n"
"Dead Neighbor Polling interval\n"
"Seconds\n")
{
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
int idx_ipv4 = 1;
int idx_pri = 3;
int idx_poll = 5;
struct in_addr nbr_addr;
unsigned int priority = OSPF_NEIGHBOR_PRIORITY_DEFAULT;
unsigned int interval = OSPF_POLL_INTERVAL_DEFAULT;
if (!inet_aton(argv[idx_ipv4]->arg, &nbr_addr)) {
vty_out(vty, "Please specify Neighbor ID by A.B.C.D\n");
return CMD_WARNING_CONFIG_FAILED;
if (no)
ospf_nbr_nbma_unset(ospf, nbr_address);
else {
ospf_nbr_nbma_set(ospf, nbr_address);
if (priority_str)
ospf_nbr_nbma_priority_set(ospf, nbr_address, priority);
if (interval_str)
ospf_nbr_nbma_poll_interval_set(ospf, nbr_address,
interval);
}
if (argc > 2)
priority = strtoul(argv[idx_pri]->arg, NULL, 10);
if (argc > 4)
interval = strtoul(argv[idx_poll]->arg, NULL, 10);
ospf_nbr_nbma_set(ospf, nbr_addr);
if (argc > 2)
ospf_nbr_nbma_priority_set(ospf, nbr_addr, priority);
if (argc > 4)
ospf_nbr_nbma_poll_interval_set(ospf, nbr_addr, interval);
return CMD_SUCCESS;
}
DEFUN (ospf_neighbor_poll_interval,
ospf_neighbor_poll_interval_cmd,
"neighbor A.B.C.D poll-interval (1-65535) [priority (0-255)]",
NEIGHBOR_STR
"Neighbor IP address\n"
"Dead Neighbor Polling interval\n"
"Seconds\n"
"OSPF priority of non-broadcast neighbor\n"
"Priority\n")
{
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
int idx_ipv4 = 1;
int idx_poll = 3;
int idx_pri = 5;
struct in_addr nbr_addr;
unsigned int priority;
unsigned int interval;
if (!inet_aton(argv[idx_ipv4]->arg, &nbr_addr)) {
vty_out(vty, "Please specify Neighbor ID by A.B.C.D\n");
return CMD_WARNING_CONFIG_FAILED;
}
interval = strtoul(argv[idx_poll]->arg, NULL, 10);
priority = argc > 4 ? strtoul(argv[idx_pri]->arg, NULL, 10)
: OSPF_NEIGHBOR_PRIORITY_DEFAULT;
ospf_nbr_nbma_set(ospf, nbr_addr);
ospf_nbr_nbma_poll_interval_set(ospf, nbr_addr, interval);
if (argc > 4)
ospf_nbr_nbma_priority_set(ospf, nbr_addr, priority);
return CMD_SUCCESS;
}
DEFUN (no_ospf_neighbor,
no_ospf_neighbor_cmd,
"no neighbor A.B.C.D [priority (0-255) [poll-interval (1-65525)]]",
NO_STR
NEIGHBOR_STR
"Neighbor IP address\n"
"Neighbor Priority\n"
"Priority\n"
"Dead Neighbor Polling interval\n"
"Seconds\n")
{
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
int idx_ipv4 = 2;
struct in_addr nbr_addr;
if (!inet_aton(argv[idx_ipv4]->arg, &nbr_addr)) {
vty_out(vty, "Please specify Neighbor ID by A.B.C.D\n");
return CMD_WARNING_CONFIG_FAILED;
}
(void)ospf_nbr_nbma_unset(ospf, nbr_addr);
return CMD_SUCCESS;
}
DEFUN (no_ospf_neighbor_poll,
no_ospf_neighbor_poll_cmd,
"no neighbor A.B.C.D poll-interval (1-65535) [priority (0-255)]",
NO_STR
NEIGHBOR_STR
"Neighbor IP address\n"
"Dead Neighbor Polling interval\n"
"Seconds\n"
"Neighbor Priority\n"
"Priority\n")
{
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
int idx_ipv4 = 2;
struct in_addr nbr_addr;
if (!inet_aton(argv[idx_ipv4]->arg, &nbr_addr)) {
vty_out(vty, "Please specify Neighbor ID by A.B.C.D\n");
return CMD_WARNING_CONFIG_FAILED;
}
(void)ospf_nbr_nbma_unset(ospf, nbr_addr);
return CMD_SUCCESS;
}
@ -4148,6 +4048,8 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf,
ospf_interface_auth_show(vty, oi, json_interface_sub, use_json);
ospf_interface_auth_show(vty, oi, json_oi, use_json);
/* Point-to-Multipoint Interface options. */
if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) {
if (use_json) {
json_object_boolean_add(json_interface_sub,
@ -4162,6 +4064,19 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf,
" %sDelay reflooding LSAs received on P2MP interface\n",
oi->p2mp_delay_reflood ? "" : "Don't ");
}
if (use_json) {
json_object_boolean_add(json_interface_sub,
"p2mpNonBroadcast",
oi->p2mp_non_broadcast);
json_object_boolean_add(json_oi,
"p2mpNonBroadcast",
oi->p2mp_non_broadcast);
} else {
vty_out(vty,
" P2MP interface does %ssupport broadcast\n",
oi->p2mp_non_broadcast ? "not " : "");
}
}
/* Add ospf_interface object to main json blob using SIP as key
@ -5970,7 +5885,7 @@ static int show_ip_ospf_neighbor_detail_all_common(struct vty *vty,
prev_nbr = nbr;
}
if (oi->type != OSPF_IFTYPE_NBMA)
if (!OSPF_IF_NON_BROADCAST(oi))
continue;
struct listnode *nd;
@ -8548,7 +8463,7 @@ DEFUN_HIDDEN (no_ospf_hello_interval,
DEFUN(ip_ospf_network, ip_ospf_network_cmd,
"ip ospf network <broadcast|"
"non-broadcast|"
"point-to-multipoint [delay-reflood]|"
"point-to-multipoint [delay-reflood|non-broadcast]|"
"point-to-point [dmvpn]>",
"IP Information\n"
"OSPF interface commands\n"
@ -8557,6 +8472,7 @@ DEFUN(ip_ospf_network, ip_ospf_network_cmd,
"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-multipoint network doesn't support broadcast\n"
"Specify OSPF point-to-point network\n"
"Specify OSPF point-to-point DMVPN network\n")
{
@ -8565,6 +8481,7 @@ DEFUN(ip_ospf_network, ip_ospf_network_cmd,
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;
uint8_t old_p2mp_non_broadcast = IF_DEF_PARAMS(ifp)->p2mp_non_broadcast;
struct route_node *rn;
if (old_type == OSPF_IFTYPE_LOOPBACK) {
@ -8576,16 +8493,19 @@ 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_DEF_PARAMS(ifp)->p2mp_non_broadcast = OSPF_P2MP_NON_BROADCAST_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)) {
IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_POINTOMULTIPOINT;
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 (argv_find(argv, argc, "non-broadcast", &idx))
IF_DEF_PARAMS(ifp)->p2mp_non_broadcast = true;
} 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-point", &idx)) {
IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_POINTOPOINT;
if (argv_find(argv, argc, "dmvpn", &idx))
IF_DEF_PARAMS(ifp)->ptp_dmvpn = 1;
@ -8593,7 +8513,8 @@ DEFUN(ip_ospf_network, ip_ospf_network_cmd,
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)
IF_DEF_PARAMS(ifp)->p2mp_delay_reflood == old_p2mp_delay_reflood &&
IF_DEF_PARAMS(ifp)->p2mp_non_broadcast == old_p2mp_non_broadcast)
return CMD_SUCCESS;
SET_IF_PARAM(IF_DEF_PARAMS(ifp), type);
@ -8607,13 +8528,16 @@ DEFUN(ip_ospf_network, ip_ospf_network_cmd,
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;
oi->p2mp_non_broadcast = IF_DEF_PARAMS(ifp)->p2mp_non_broadcast;
/*
* 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_DEF_PARAMS(ifp)->ptp_dmvpn != old_ptp_dmvpn ||
IF_DEF_PARAMS(ifp)->p2mp_non_broadcast !=
old_p2mp_non_broadcast) {
if (oi->state > ISM_Down) {
OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown);
OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceUp);
@ -8657,6 +8581,7 @@ DEFUN (no_ip_ospf_network,
IF_DEF_PARAMS(ifp)->ptp_dmvpn = 0;
IF_DEF_PARAMS(ifp)->p2mp_delay_reflood =
OSPF_P2MP_DELAY_REFLOOD_DEFAULT;
IF_DEF_PARAMS(ifp)->p2mp_non_broadcast = OSPF_P2MP_NON_BROADCAST_DEFAULT;
if (IF_DEF_PARAMS(ifp)->type == old_type)
return CMD_SUCCESS;
@ -12225,6 +12150,10 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf)
OSPF_IFTYPE_POINTOMULTIPOINT &&
params->p2mp_delay_reflood)
vty_out(vty, " delay-reflood");
if (params->type ==
OSPF_IFTYPE_POINTOMULTIPOINT &&
params->p2mp_non_broadcast)
vty_out(vty, " non-broadcast");
if (params != IF_DEF_PARAMS(ifp) && rn)
vty_out(vty, " %pI4",
&rn->p.u.prefix4);
@ -13753,11 +13682,8 @@ void ospf_vty_init(void)
install_element(OSPF_NODE, &ospf_auto_cost_reference_bandwidth_cmd);
install_element(OSPF_NODE, &no_ospf_auto_cost_reference_bandwidth_cmd);
/* "neighbor" commands. */
/* "neighbor" command. */
install_element(OSPF_NODE, &ospf_neighbor_cmd);
install_element(OSPF_NODE, &ospf_neighbor_poll_interval_cmd);
install_element(OSPF_NODE, &no_ospf_neighbor_cmd);
install_element(OSPF_NODE, &no_ospf_neighbor_poll_cmd);
/* write multiplier commands */
install_element(OSPF_NODE, &ospf_write_multiplier_cmd);

View File

@ -1096,6 +1096,7 @@ struct ospf_interface *add_ospf_interface(struct connected *co,
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;
oi->p2mp_non_broadcast = IF_DEF_PARAMS(co->ifp)->p2mp_non_broadcast;
/* Add pseudo neighbor. */
ospf_nbr_self_reset(oi, oi->ospf->router_id);
@ -1989,7 +1990,7 @@ static void ospf_nbr_nbma_add(struct ospf_nbr_nbma *nbr_nbma,
struct route_node *rn;
struct prefix p;
if (oi->type != OSPF_IFTYPE_NBMA)
if (!OSPF_IF_NON_BROADCAST(oi))
return;
if (nbr_nbma->nbr != NULL)
@ -2036,7 +2037,7 @@ void ospf_nbr_nbma_if_update(struct ospf *ospf, struct ospf_interface *oi)
struct route_node *rn;
struct prefix_ipv4 p;
if (oi->type != OSPF_IFTYPE_NBMA)
if (!OSPF_IF_NON_BROADCAST(oi))
return;
for (rn = route_top(ospf->nbr_nbma); rn; rn = route_next(rn))
@ -2095,7 +2096,7 @@ int ospf_nbr_nbma_set(struct ospf *ospf, struct in_addr nbr_addr)
rn->info = nbr_nbma;
for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) {
if (oi->type == OSPF_IFTYPE_NBMA)
if (OSPF_IF_NON_BROADCAST(oi))
if (prefix_match(oi->address, (struct prefix *)&p)) {
ospf_nbr_nbma_add(nbr_nbma, oi);
break;

View File

@ -0,0 +1,26 @@
!
hostname r1
password zebra
log file /tmp/r1-frr.log
ip forwarding
!
interface r1-eth0
ip address 10.1.0.1/24
ip ospf network point-to-multipoint non-broadcast
ip ospf hello-interval 1
ip ospf dead-interval 30
!
interface r1-eth1
ip address 10.1.1.1/24
ip ospf hello-interval 1
ip ospf dead-interval 30
!
router ospf
ospf router-id 1.1.1.1
distance 20
network 10.1.0.0/24 area 0
network 10.1.1.0/24 area 0
neighbor 10.1.0.2 poll-interval 5
neighbor 10.1.0.3 poll-interval 5
neighbor 10.1.0.4 poll-interval 5
!

View File

@ -0,0 +1,23 @@
!
hostname r1
password zebra
log file /tmp/r1-frr.log
ip forwarding
!
interface r1-eth0
ip address 10.1.0.1/24
ip ospf network point-to-multipoint
ip ospf hello-interval 1
ip ospf dead-interval 30
!
interface r1-eth1
ip address 10.1.1.1/24
ip ospf hello-interval 1
ip ospf dead-interval 30
!
router ospf
ospf router-id 1.1.1.1
distance 20
network 10.1.0.0/24 area 0
network 10.1.1.0/24 area 0
!

View File

@ -0,0 +1,29 @@
!
hostname r2
password zebra
log file /tmp/r1-frr.log
ip forwarding
!
interface r2-eth0
ip address 10.1.0.2/24
ip ospf network point-to-multipoint non-broadcast
ip ospf hello-interval 1
ip ospf dead-interval 30
!
interface r2-eth1
ip address 10.1.2.2/24
ip ospf hello-interval 1
ip ospf dead-interval 30
!
!
!
!
router ospf
ospf router-id 2.2.2.2
distance 20
network 10.1.0.0/24 area 0
network 10.1.2.0/24 area 0
neighbor 10.1.0.1 poll-interval 5
neighbor 10.1.0.3 poll-interval 5
neighbor 10.1.0.4 poll-interval 5
!

View File

@ -0,0 +1,26 @@
!
hostname r2
password zebra
log file /tmp/r1-frr.log
ip forwarding
!
interface r2-eth0
ip address 10.1.0.2/24
ip ospf network point-to-multipoint
ip ospf hello-interval 1
ip ospf dead-interval 30
!
interface r2-eth1
ip address 10.1.2.2/24
ip ospf hello-interval 1
ip ospf dead-interval 30
!
!
!
!
router ospf
ospf router-id 2.2.2.2
distance 20
network 10.1.0.0/24 area 0
network 10.1.2.0/24 area 0
!

View File

@ -0,0 +1,28 @@
!
hostname r3
password zebra
log file /tmp/r1-frr.log
ip forwarding
!
interface r3-eth0
ip address 10.1.0.3/24
ip ospf network point-to-multipoint non-broadcast
ip ospf hello-interval 1
ip ospf dead-interval 30
!
!
interface r3-eth1
ip address 10.1.3.3/24
ip ospf network broadcast
ip ospf hello-interval 1
ip ospf dead-interval 30
!
!
router ospf
ospf router-id 3.3.3.3
distance 20
network 10.1.0.0/24 area 0
network 10.1.3.0/24 area 0
neighbor 10.1.0.1 poll-interval 5
neighbor 10.1.0.2 poll-interval 5
neighbor 10.1.0.4 poll-interval 5

View File

@ -0,0 +1,25 @@
!
hostname r3
password zebra
log file /tmp/r1-frr.log
ip forwarding
!
interface r3-eth0
ip address 10.1.0.3/24
ip ospf network point-to-multipoint
ip ospf hello-interval 1
ip ospf dead-interval 30
!
!
interface r3-eth1
ip address 10.1.3.3/24
ip ospf network broadcast
ip ospf hello-interval 1
ip ospf dead-interval 30
!
!
router ospf
ospf router-id 3.3.3.3
distance 20
network 10.1.0.0/24 area 0
network 10.1.3.0/24 area 0

View File

@ -0,0 +1,28 @@
!
hostname r4
password zebra
log file /tmp/r1-frr.log
ip forwarding
!
interface r4-eth0
ip address 10.1.0.4/24
ip ospf network point-to-multipoint non-broadcast
ip ospf hello-interval 1
ip ospf dead-interval 30
!
!
interface r4-eth1
ip address 10.1.4.4/24
ip ospf network broadcast
ip ospf hello-interval 1
ip ospf dead-interval 30
!
!
router ospf
ospf router-id 4.4.4.4
distance 20
network 10.1.0.0/24 area 0
network 10.1.4.0/24 area 0
neighbor 10.1.0.1 poll-interval 5
neighbor 10.1.0.2 poll-interval 5
neighbor 10.1.0.3 poll-interval 5

View File

@ -0,0 +1,25 @@
!
hostname r4
password zebra
log file /tmp/r1-frr.log
ip forwarding
!
interface r4-eth0
ip address 10.1.0.4/24
ip ospf network point-to-multipoint
ip ospf hello-interval 1
ip ospf dead-interval 30
!
!
interface r4-eth1
ip address 10.1.4.4/24
ip ospf network broadcast
ip ospf hello-interval 1
ip ospf dead-interval 30
!
!
router ospf
ospf router-id 4.4.4.4
distance 20
network 10.1.0.0/24 area 0
network 10.1.4.0/24 area 0

View File

@ -0,0 +1,346 @@
#!/usr/bin/env python
# SPDX-License-Identifier: ISC
#
# test_ospf_prefix_p2mp_broadcast.py
#
# Copyright (c) 2024 LabN Consulting
# Acee Lindem
#
import os
import sys
import json
from time import sleep
from functools import partial
import pytest
# pylint: disable=C0413
# Import topogen and topotest helpers
from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from lib.common_config import (
run_frr_cmd,
shutdown_bringup_interface,
start_router_daemons,
step,
)
"""
test_ospf_p2mp_broadcast.py: Test OSPF Point-to-multipoint
"""
TOPOLOGY = """
+-----+ +-----+
10.1.1.0/24 | r1 | | r2 | 10.1.2.0/24
-----------+ | | +----------
+--+--+ +--+--+
| 10.1.0.0/24 |
| +-------+ |
+---- | |-----+
| P2MP |
+---- | |-----+
| +-------+ |
| |
| |
+--+--+ +-+---+
10.1.3.0/24 | r3 | | r4 | 10.1.4.0/24
-----------+ | | +----------
+-----+ +-----+
"""
# Save the Current Working Directory to find configuration files.
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, "../"))
# Required to instantiate the topology builder class.
pytestmark = [pytest.mark.ospfd, pytest.mark.bgpd]
def build_topo(tgen):
"Build function"
# Create 4 routers
tgen.add_router("r1")
tgen.add_router("r2")
tgen.add_router("r3")
tgen.add_router("r4")
# Interconect them all to the P2MP network
switch = tgen.add_switch("s0-p2mp")
switch.add_link(tgen.gears["r1"])
switch.add_link(tgen.gears["r2"])
switch.add_link(tgen.gears["r3"])
switch.add_link(tgen.gears["r4"])
# Add standalone network to router 1
switch = tgen.add_switch("s-r1-1")
switch.add_link(tgen.gears["r1"])
# Add standalone network to router 2
switch = tgen.add_switch("s-r2-1")
switch.add_link(tgen.gears["r2"])
# Add standalone network to router 3
switch = tgen.add_switch("s-r3-1")
switch.add_link(tgen.gears["r3"])
# Add standalone network to router 4
switch = tgen.add_switch("s-r4-1")
switch.add_link(tgen.gears["r4"])
def setup_module(mod):
logger.info("OSPF Point-to-MultiPoint:\n {}".format(TOPOLOGY))
tgen = Topogen(build_topo, mod.__name__)
tgen.start_topology()
# Starting Routers
router_list = tgen.routers()
for rname, router in router_list.items():
logger.info("Loading router %s" % rname)
router.load_frr_config(os.path.join(CWD, "{}/frr-p2mp.conf".format(rname)))
# Initialize all routers.
tgen.start_router()
def teardown_module(mod):
"Teardown the pytest environment"
tgen = get_topogen()
tgen.stop_topology()
def verify_p2mp_interface(tgen):
"Verify the P2MP Configuration and interface settings"
r1 = tgen.gears["r1"]
step("Test running configuration for P2MP configuration")
rc = 0
rc, _, _ = tgen.net["r1"].cmd_status(
"show running ospfd | grep 'ip ospf network point-to-multipoint'", warn=False
)
assertmsg = "'ip ospf network point-to-multipoint' applied, but not present in r1 configuration"
assert rc, assertmsg
step("Test OSPF interface for P2MP settings")
input_dict = {
"interfaces": {
"r1-eth0": {
"ospfEnabled": True,
"interfaceIp": {
"10.1.0.1": {
"ipAddress": "10.1.0.1",
"ipAddressPrefixlen": 24,
"ospfIfType": "Broadcast",
"routerId": "1.1.1.1",
"networkType": "POINTOMULTIPOINT",
"cost": 10,
"state": "Point-To-Point",
"nbrCount": 3,
"nbrAdjacentCount": 3,
"prefixSuppression": False,
"p2mpDelayReflood": False,
"p2mpNonBroadcast": False,
}
},
"ipAddress": "10.1.0.1",
"ipAddressPrefixlen": 24,
"ospfIfType": "Broadcast",
"area": "0.0.0.0",
"routerId": "1.1.1.1",
"networkType": "POINTOMULTIPOINT",
"cost": 10,
"state": "Point-To-Point",
"opaqueCapable": True,
"nbrCount": 3,
"nbrAdjacentCount": 3,
"prefixSuppression": False,
"p2mpDelayReflood": False,
"p2mpNonBroadcast": False,
}
}
}
test_func = partial(
topotest.router_json_cmp, r1, "show ip ospf interface r1-eth0 json", input_dict
)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assertmsg = "P2MP Interface Mismatch on router r1"
assert result is None, assertmsg
def verify_non_p2mp_interface(tgen):
"Verify the removal of P2MP Configuration and interface settings"
r1 = tgen.gears["r1"]
step("Test running configuration for removal of P2MP configuration")
rc = 0
rc, _, _ = tgen.net["r1"].cmd_status(
"show running ospfd | grep -q 'ip ospf network point-to-multipoint'", warn=False
)
assertmsg = "'ip ospf network point-to-multipoint' not applied, but present in r1 configuration"
assert rc, assertmsg
step("Test OSPF interface for default settings")
input_dict = {
"interfaces": {
"r1-eth0": {
"ospfEnabled": True,
"interfaceIp": {
"10.1.0.1": {
"ipAddress": "10.1.0.1",
"ipAddressPrefixlen": 24,
"ospfIfType": "Broadcast",
"routerId": "1.1.1.1",
"networkType": "BROADCAST",
"cost": 10,
"prefixSuppression": False,
}
},
"ipAddress": "10.1.0.1",
"ipAddressPrefixlen": 24,
"ospfIfType": "Broadcast",
"area": "0.0.0.0",
"routerId": "1.1.1.1",
"networkType": "BROADCAST",
"cost": 10,
"opaqueCapable": True,
"prefixSuppression": False,
}
}
}
test_func = partial(
topotest.router_json_cmp, r1, "show ip ospf interface r1-eth0 json", input_dict
)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assertmsg = "P2MP Interface Mismatch on router r1"
assert result is None, assertmsg
def verify_p2mp_neighbor(tgen, router, neighbor, state, intf_addr, interface):
topo_router = tgen.gears[router]
step("Verify neighbor " + neighbor + " in " + state + " state")
input_dict = {
"default": {
neighbor: [
{
"nbrState": state,
"ifaceAddress": intf_addr,
"ifaceName": interface,
}
],
}
}
test_func = partial(
topotest.router_json_cmp,
topo_router,
"show ip ospf neighbor " + neighbor + " json",
input_dict,
)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assertmsg = "P2MP Neighbor " + neighbor + " not in " + state
assert result is None, assertmsg
def verify_p2mp_route(tgen, router, prefix, prefix_len, nexthop, interface):
topo_router = tgen.gears[router]
step("Verify router " + router + " p2mp route " + prefix + " installed")
input_dict = {
prefix: [
{
"prefix": prefix,
"prefixLen": prefix_len,
"protocol": "ospf",
"nexthops": [
{
"ip": nexthop,
"interfaceName": interface,
}
],
}
]
}
test_func = partial(
topotest.router_json_cmp,
topo_router,
"show ip route " + prefix + " json",
input_dict,
)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assertmsg = prefix + " not installed on router " + router
assert result is None, assertmsg
def test_p2mp_broadcast_interface():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip("Skipped because of router(s) failure")
step("Verify router r1 interface r1-eth0 p2mp configuration")
verify_p2mp_interface(tgen)
step("Verify router r1 p2mp interface r1-eth0 neighbors")
verify_p2mp_neighbor(
tgen, "r1", "2.2.2.2", "Full/DROther", "10.1.0.2", "r1-eth0:10.1.0.1"
)
verify_p2mp_neighbor(
tgen, "r1", "3.3.3.3", "Full/DROther", "10.1.0.3", "r1-eth0:10.1.0.1"
)
verify_p2mp_neighbor(
tgen, "r1", "4.4.4.4", "Full/DROther", "10.1.0.4", "r1-eth0:10.1.0.1"
)
step("Verify router r1 p2mp routes installed")
verify_p2mp_route(tgen, "r1", "10.1.2.0/24", 24, "10.1.0.2", "r1-eth0")
verify_p2mp_route(tgen, "r1", "10.1.3.0/24", 24, "10.1.0.3", "r1-eth0")
verify_p2mp_route(tgen, "r1", "10.1.4.0/24", 24, "10.1.0.4", "r1-eth0")
step("Verify router r1 interface r1-eth0 p2mp configuration removal")
r1 = tgen.gears["r1"]
r1.vtysh_cmd("conf t\ninterface r1-eth0\nno ip ospf network point-to-multipoint")
verify_non_p2mp_interface(tgen)
step("Verify router r1 interface r1-eth0 p2mp configuration application")
r1.vtysh_cmd("conf t\ninterface r1-eth0\nip ospf network point-to-multipoint")
verify_p2mp_interface(tgen)
step("Verify restablishment of r1-eth0 p2mp neighbors")
verify_p2mp_neighbor(
tgen, "r1", "2.2.2.2", "Full/DROther", "10.1.0.2", "r1-eth0:10.1.0.1"
)
verify_p2mp_neighbor(
tgen, "r1", "3.3.3.3", "Full/DROther", "10.1.0.3", "r1-eth0:10.1.0.1"
)
verify_p2mp_neighbor(
tgen, "r1", "4.4.4.4", "Full/DROther", "10.1.0.4", "r1-eth0:10.1.0.1"
)
step("Verify router r1 p2mp routes reinstalled")
verify_p2mp_route(tgen, "r1", "10.1.2.0/24", 24, "10.1.0.2", "r1-eth0")
verify_p2mp_route(tgen, "r1", "10.1.3.0/24", 24, "10.1.0.3", "r1-eth0")
verify_p2mp_route(tgen, "r1", "10.1.4.0/24", 24, "10.1.0.4", "r1-eth0")
def test_memory_leak():
"Run the memory leak test and report results."
tgen = get_topogen()
if not tgen.is_memleak_enabled():
pytest.skip("Memory leak test/report is disabled")
tgen.report_memory_leaks()
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))

View File

@ -0,0 +1,467 @@
#!/usr/bin/env python
# SPDX-License-Identifier: ISC
#
# test_ospf_prefix_p2mp_non_broadcast.py
#
# Copyright (c) 2024 LabN Consulting
# Acee Lindem
#
import os
import sys
import json
from time import sleep
from functools import partial
import pytest
# pylint: disable=C0413
# Import topogen and topotest helpers
from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from lib.common_config import (
run_frr_cmd,
shutdown_bringup_interface,
start_router_daemons,
step,
)
"""
test_ospf_p2mp_non_broadcast.py: Test OSPF Point-to-multipoint Non-Broadcast
Full Mesh
"""
TOPOLOGY = """
+-----+ +-----+
10.1.1.0/24 | r1 | | r2 | 10.1.2.0/24
-----------+ | | +----------
+--+--+ +--+--+
| 10.1.0.0/24 |
| +-------+ |
+---- | |-----+
| P2MP |
+---- | |-----+
| +-------+ |
| |
| |
+--+--+ +-+---+
10.1.3.0/24 | r3 | | r4 | 10.1.4.0/24
-----------+ | | +----------
+-----+ +-----+
"""
# Save the Current Working Directory to find configuration files.
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, "../"))
# Required to instantiate the topology builder class.
pytestmark = [pytest.mark.ospfd, pytest.mark.bgpd]
def build_topo(tgen):
"Build function"
# Create 4 routers
tgen.add_router("r1")
tgen.add_router("r2")
tgen.add_router("r3")
tgen.add_router("r4")
# Interconect them all to the P2MP network
switch = tgen.add_switch("s0-p2mp")
switch.add_link(tgen.gears["r1"])
switch.add_link(tgen.gears["r2"])
switch.add_link(tgen.gears["r3"])
switch.add_link(tgen.gears["r4"])
# Add standalone network to router 1
switch = tgen.add_switch("s-r1-1")
switch.add_link(tgen.gears["r1"])
# Add standalone network to router 2
switch = tgen.add_switch("s-r2-1")
switch.add_link(tgen.gears["r2"])
# Add standalone network to router 3
switch = tgen.add_switch("s-r3-1")
switch.add_link(tgen.gears["r3"])
# Add standalone network to router 4
switch = tgen.add_switch("s-r4-1")
switch.add_link(tgen.gears["r4"])
def setup_module(mod):
logger.info("OSPF Point-to-MultiPoint Non-Broadcast:\n {}".format(TOPOLOGY))
tgen = Topogen(build_topo, mod.__name__)
tgen.start_topology()
# Starting Routers
router_list = tgen.routers()
for rname, router in router_list.items():
logger.info("Loading router %s" % rname)
router.load_frr_config(
os.path.join(CWD, "{}/frr-p2mp-non-broadcast.conf".format(rname))
)
# Initialize all routers.
tgen.start_router()
def teardown_module(mod):
"Teardown the pytest environment"
tgen = get_topogen()
tgen.stop_topology()
def verify_p2mp_interface(tgen, router, nbr_cnt, nbr_adj_cnt, non_broadcast):
"Verify the P2MP Configuration and interface settings"
topo_router = tgen.gears[router]
step("Test running configuration for P2MP configuration")
rc = 0
rc, _, _ = tgen.net[router].cmd_status(
"show running ospfd | grep 'ip ospf network point-to-multipoint'", warn=False
)
assertmsg = (
"'ip ospf network point-to-multipoint' applied, but not present in "
+ router
+ "configuration"
)
assert rc, assertmsg
step("Test OSPF interface for P2MP settings")
input_dict = {
"interfaces": {
"r1-eth0": {
"ospfEnabled": True,
"interfaceIp": {
"10.1.0.1": {
"ipAddress": "10.1.0.1",
"ipAddressPrefixlen": 24,
"ospfIfType": "Broadcast",
"routerId": "1.1.1.1",
"networkType": "POINTOMULTIPOINT",
"cost": 10,
"state": "Point-To-Point",
"nbrCount": nbr_cnt,
"nbrAdjacentCount": nbr_adj_cnt,
"prefixSuppression": False,
"p2mpDelayReflood": False,
"p2mpNonBroadcast": non_broadcast,
}
},
"ipAddress": "10.1.0.1",
"ipAddressPrefixlen": 24,
"ospfIfType": "Broadcast",
"area": "0.0.0.0",
"routerId": "1.1.1.1",
"networkType": "POINTOMULTIPOINT",
"cost": 10,
"state": "Point-To-Point",
"opaqueCapable": True,
"nbrCount": nbr_cnt,
"nbrAdjacentCount": nbr_adj_cnt,
"prefixSuppression": False,
"p2mpDelayReflood": False,
"p2mpNonBroadcast": non_broadcast,
}
}
}
test_func = partial(
topotest.router_json_cmp,
topo_router,
"show ip ospf interface r1-eth0 json",
input_dict,
)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assertmsg = "P2MP Interface Mismatch on router r1"
assert result is None, assertmsg
def verify_p2mp_neighbor(tgen, router, neighbor, state, intf_addr, interface):
topo_router = tgen.gears[router]
step("Verify neighbor " + neighbor + " in " + state + " state")
input_dict = {
"default": {
neighbor: [
{
"nbrState": state,
"ifaceAddress": intf_addr,
"ifaceName": interface,
}
],
}
}
test_func = partial(
topotest.router_json_cmp,
topo_router,
"show ip ospf neighbor " + neighbor + " json",
input_dict,
)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assertmsg = "P2MP Neighbor " + neighbor + " not in " + state
assert result is None, assertmsg
def verify_p2mp_route(tgen, router, prefix, prefix_len, nexthop, interface):
topo_router = tgen.gears[router]
step("Verify router " + router + " p2mp route " + prefix + " installed")
input_dict = {
prefix: [
{
"prefix": prefix,
"prefixLen": prefix_len,
"protocol": "ospf",
"nexthops": [
{
"ip": nexthop,
"interfaceName": interface,
}
],
}
]
}
test_func = partial(
topotest.router_json_cmp,
topo_router,
"show ip route " + prefix + " json",
input_dict,
)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assertmsg = prefix + " not installed on router " + router
assert result is None, assertmsg
def test_p2mp_non_broadcast_connectivity():
tgen = get_topogen()
r1 = tgen.gears["r1"]
if tgen.routers_have_failure():
pytest.skip("Skipped because of router(s) failure")
step("Verify router r1 interface OSPF point-to-multipoint non-broadcast interface")
verify_p2mp_interface(tgen, "r1", 3, 3, True)
step("Verify router r1 interface r1-eth0 p2mp non-broadcast configuration")
rc, _, _ = tgen.net["r1"].cmd_status(
"show running ospfd | grep -q 'ip ospf network point-to-multipoint non-broadcast'",
warn=False,
)
assertmsg = "'ip ospf network point-to-multipoint non-broadcast' applied, but not present in R1 configuration"
assert rc, assertmsg
step("Verify router r1 OSPF point-to-multipoint neighbors")
verify_p2mp_neighbor(
tgen, "r1", "2.2.2.2", "Full/DROther", "10.1.0.2", "r1-eth0:10.1.0.1"
)
verify_p2mp_neighbor(
tgen, "r1", "3.3.3.3", "Full/DROther", "10.1.0.3", "r1-eth0:10.1.0.1"
)
verify_p2mp_neighbor(
tgen, "r1", "4.4.4.4", "Full/DROther", "10.1.0.4", "r1-eth0:10.1.0.1"
)
step("Verify router r1 OSPF point-to-multipoint routes are installed")
verify_p2mp_route(tgen, "r1", "10.1.2.0/24", 24, "10.1.0.2", "r1-eth0")
verify_p2mp_route(tgen, "r1", "10.1.3.0/24", 24, "10.1.0.3", "r1-eth0")
verify_p2mp_route(tgen, "r1", "10.1.4.0/24", 24, "10.1.0.4", "r1-eth0")
step("Remove r1 interface r1-eth0 p2mp non-broadcast configuration")
r1.vtysh_cmd("conf t\ninterface r1-eth0\nip ospf network point-to-multipoint")
rc, _, _ = tgen.net["r1"].cmd_status(
"show running ospfd | grep -q 'ip ospf network point-to-multipoint non-broadcast'",
warn=False,
)
assertmsg = "'ip ospf network point-to-multipoint non-broadcast' not applied, but present in r1 configuration"
assert rc, assertmsg
step("Verify router r1 interface OSPF point-to-multipoint broadcast interface")
verify_p2mp_interface(tgen, "r1", 3, 3, False)
step("Add r1 interface r1-eth0 p2mp non-broadcast configuration back")
r1.vtysh_cmd(
"conf t\ninterface r1-eth0\nip ospf network point-to-multipoint non-broadcast"
)
rc, _, _ = tgen.net["r1"].cmd_status(
"show running ospfd | grep 'ip ospf network point-to-multipoint non-broadcast'",
warn=False,
)
assertmsg = "'ip ospf netrwork point-to-multipoint non-broadcast' applied, but not present in R1 configuration"
assert rc, assertmsg
step("Verify router r1 interface OSPF point-to-multipoint non-broadcast interface")
verify_p2mp_interface(tgen, "r1", 3, 3, True)
step(
"Verify router r1 OSPF point-to-multipoint neighbors adjacencies restablished."
)
verify_p2mp_neighbor(
tgen, "r1", "2.2.2.2", "Full/DROther", "10.1.0.2", "r1-eth0:10.1.0.1"
)
verify_p2mp_neighbor(
tgen, "r1", "3.3.3.3", "Full/DROther", "10.1.0.3", "r1-eth0:10.1.0.1"
)
verify_p2mp_neighbor(
tgen, "r1", "4.4.4.4", "Full/DROther", "10.1.0.4", "r1-eth0:10.1.0.1"
)
def test_p2mp_non_broadcast_partial_mesh_connectivity():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip("Skipped because of router(s) failure")
"""
test_ospf_p2mp_non_broadcast.py: Test OSPF Point-to-multipoint Non-Broadcast
Partial Mesh
"""
TOPOLOGY = """
+-----+ +------+
10.1.1.0/24 | r1 | | r4 | 10.1.4.0/24
-----------+ | | +----------
+-+---+ +--+-+-+
| P2MP |
| Non-Broadcast |
| 10.1.0.0/24 |
+-+---+ +-+---+
10.1.2.0/24 | r2 | | r3 | 10.1.3.0/24
-----------+ +--------------+ +----------
+-----+ +-----+
"""
logger.info("OSPF Point-to-MultiPoint Non-Broadcast:\n {}".format(TOPOLOGY))
step("Change configuration to a partial mesh")
step("Delete neighbors in full mesh")
r1 = tgen.gears["r1"]
r1.vtysh_cmd("conf t\nrouter ospf\nno neighbor 10.1.0.3")
r1.vtysh_cmd("conf t\nrouter ospf\nno neighbor 10.1.0.4")
r2 = tgen.gears["r2"]
r2.vtysh_cmd("conf t\nrouter ospf\nno neighbor 10.1.0.4")
r3 = tgen.gears["r3"]
r3.vtysh_cmd("conf t\nrouter ospf\nno neighbor 10.1.0.1")
r4 = tgen.gears["r4"]
r4.vtysh_cmd("conf t\nrouter ospf\nno neighbor 10.1.0.1")
r4.vtysh_cmd("conf t\nrouter ospf\nno neighbor 10.1.0.2")
step("Flap interfaces on P2MP network to avoid transients")
r1.vtysh_cmd("conf t\ninterface r1-eth0\nshut")
r2.vtysh_cmd("conf t\ninterface r2-eth0\nshut")
r3.vtysh_cmd("conf t\ninterface r3-eth0\nshut")
r4.vtysh_cmd("conf t\ninterface r4-eth0\nshut")
r1.vtysh_cmd("conf t\ninterface r1-eth0\nno shut")
r2.vtysh_cmd("conf t\ninterface r2-eth0\nno shut")
r3.vtysh_cmd("conf t\ninterface r3-eth0\nno shut")
r4.vtysh_cmd("conf t\ninterface r4-eth0\nno shut")
step("Verify router r1 interface OSPF point-to-multipoint non-broadcast interface")
verify_p2mp_interface(tgen, "r1", 1, 1, True)
step("Verify router r1 interface r1-eth0 p2mp neighbor")
verify_p2mp_neighbor(
tgen, "r1", "2.2.2.2", "Full/DROther", "10.1.0.2", "r1-eth0:10.1.0.1"
)
step("Verify router r1 p2mp routes are installed")
verify_p2mp_route(tgen, "r1", "10.1.2.0/24", 24, "10.1.0.2", "r1-eth0")
verify_p2mp_route(tgen, "r1", "10.1.3.0/24", 24, "10.1.0.2", "r1-eth0")
verify_p2mp_route(tgen, "r1", "10.1.4.0/24", 24, "10.1.0.2", "r1-eth0")
"""
test_ospf_p2mp_non_broadcast.py: Test OSPF Point-to-multipoint Non-Broadcast
Modified Partial Mesh
"""
TOPOLOGY = """
+-----+ +------+
10.1.1.0/24 | r1 | | r4 | 10.1.4.0/24
-----------+ +-------------+ +----------
+-----+ +--+-+-+
P2MP |
Non-Broadcast |
10.1.0.0/24 |
+-+---+ +-+---+
10.1.2.0/24 | r2 | | r3 | 10.1.3.0/24
-----------+ +--------------+ +----------
+-----+ +-----+
"""
logger.info("OSPF Point-to-MultiPoint Non-Broadcast:\n {}".format(TOPOLOGY))
step("Change configuration to a partial mesh")
step("Modify neighbors in partial mesh")
r1 = tgen.gears["r1"]
r1.vtysh_cmd("conf t\nrouter ospf\nno neighbor 10.1.0.2")
r1.vtysh_cmd("conf t\nrouter ospf\nneighbor 10.1.0.4 poll-interval 5")
r2 = tgen.gears["r2"]
r2.vtysh_cmd("conf t\nrouter ospf\nno neighbor 10.1.0.1")
r4 = tgen.gears["r4"]
r4.vtysh_cmd("conf t\nrouter ospf\nneighbor 10.1.0.1")
step("Flap interfaces on P2MP network to avoid transients")
r1.vtysh_cmd("conf t\ninterface r1-eth0\nshut")
r2.vtysh_cmd("conf t\ninterface r2-eth0\nshut")
r3.vtysh_cmd("conf t\ninterface r3-eth0\nshut")
r4.vtysh_cmd("conf t\ninterface r4-eth0\nshut")
r1.vtysh_cmd("conf t\ninterface r1-eth0\nno shut")
r2.vtysh_cmd("conf t\ninterface r2-eth0\nno shut")
r3.vtysh_cmd("conf t\ninterface r3-eth0\nno shut")
r4.vtysh_cmd("conf t\ninterface r4-eth0\nno shut")
step("Verify router r1 interface r1-eth0")
step("Verify router r1 interface OSPF point-to-multipoint non-broadcast interface")
verify_p2mp_interface(tgen, "r1", 1, 1, True)
step("Verify router r1 interface r1-eth0 p2mp neighbor")
input_dict = {
"neighbors": {
"4.4.4.4": [
{
"nbrState": "Full/DROther",
"ifaceAddress": "10.1.0.4",
"ifaceName": "r1-eth0:10.1.0.1",
}
],
}
}
test_func = partial(
topotest.router_json_cmp, r1, "show ip ospf neighbor json", input_dict
)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assertmsg = "P2MP Non-Broadcast Neighbors not adjacent on router r1"
assert result is None, assertmsg
step("Verify router r1 interface r1-eth0 p2mp routes are installed")
verify_p2mp_route(tgen, "r1", "10.1.2.0/24", 24, "10.1.0.4", "r1-eth0")
verify_p2mp_route(tgen, "r1", "10.1.3.0/24", 24, "10.1.0.4", "r1-eth0")
verify_p2mp_route(tgen, "r1", "10.1.4.0/24", 24, "10.1.0.4", "r1-eth0")
def test_memory_leak():
"Run the memory leak test and report results."
tgen = get_topogen()
if not tgen.is_memleak_enabled():
pytest.skip("Memory leak test/report is disabled")
tgen.report_memory_leaks()
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))