summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am22
-rw-r--r--bgpd/bgp_attr.c7
-rw-r--r--bgpd/bgp_bfd.c4
-rw-r--r--bgpd/bgp_community.c15
-rw-r--r--bgpd/bgp_community_alias.c29
-rw-r--r--bgpd/bgp_community_alias.h1
-rw-r--r--bgpd/bgp_conditional_adv.c4
-rw-r--r--bgpd/bgp_conditional_adv.h2
-rw-r--r--bgpd/bgp_ecommunity.c12
-rw-r--r--bgpd/bgp_evpn.c20
-rw-r--r--bgpd/bgp_fsm.c10
-rw-r--r--bgpd/bgp_lcommunity.c8
-rw-r--r--bgpd/bgp_mplsvpn.c4
-rw-r--r--bgpd/bgp_nht.c13
-rw-r--r--bgpd/bgp_nht.h3
-rw-r--r--bgpd/bgp_route.c174
-rw-r--r--bgpd/bgp_routemap.c105
-rw-r--r--bgpd/bgp_routemap_nb.c7
-rw-r--r--bgpd/bgp_routemap_nb.h4
-rw-r--r--bgpd/bgp_routemap_nb_config.c56
-rw-r--r--bgpd/bgp_rpki.c8
-rw-r--r--bgpd/bgp_updgrp_packet.c2
-rw-r--r--bgpd/bgp_vty.c32
-rw-r--r--bgpd/bgp_zebra.c3
-rw-r--r--bgpd/bgpd.c19
-rw-r--r--bgpd/bgpd.h1
-rw-r--r--bgpd/subdir.am6
-rw-r--r--configure.ac10
-rw-r--r--doc/developer/workflow.rst98
-rw-r--r--doc/user/bgp.rst47
-rw-r--r--doc/user/filter.rst9
-rw-r--r--doc/user/ospf6d.rst65
-rw-r--r--doc/user/ospfd.rst24
-rw-r--r--doc/user/pim.rst8
-rw-r--r--fpm/subdir.am2
-rw-r--r--grpc/subdir.am2
-rw-r--r--isisd/isis_lsp.c104
-rw-r--r--isisd/isis_lsp.h2
-rw-r--r--isisd/isis_spf.c4
-rw-r--r--isisd/isisd.c16
-rw-r--r--isisd/isisd.h1
-rw-r--r--isisd/subdir.am2
-rw-r--r--ldpd/subdir.am2
-rw-r--r--lib/command.c3
-rw-r--r--lib/command.h2
-rw-r--r--lib/plist.c66
-rw-r--r--lib/plist.h15
-rw-r--r--lib/prefix.h32
-rw-r--r--lib/route_types.txt4
-rw-r--r--lib/routemap.h1
-rw-r--r--lib/routemap_cli.c5
-rw-r--r--lib/subdir.am17
-rw-r--r--mlag/subdir.am2
-rw-r--r--ospf6d/ospf6_abr.c19
-rw-r--r--ospf6d/ospf6_asbr.c59
-rw-r--r--ospf6d/ospf6_flood.c10
-rw-r--r--ospf6d/ospf6_interface.c96
-rw-r--r--ospf6d/ospf6_interface.h1
-rw-r--r--ospf6d/ospf6_intra.c42
-rw-r--r--ospf6d/ospf6_message.c15
-rw-r--r--ospf6d/ospf6_nssa.c2
-rw-r--r--ospf6d/ospf6_route.c9
-rw-r--r--ospf6d/ospf6_route.h6
-rw-r--r--ospf6d/ospf6_spf.c28
-rw-r--r--ospf6d/ospf6_top.c18
-rw-r--r--ospf6d/ospf6_top.h4
-rw-r--r--ospf6d/ospf6_zebra.c33
-rw-r--r--ospf6d/subdir.am2
-rw-r--r--ospfclient/subdir.am2
-rw-r--r--ospfd/ospf_abr.c6
-rw-r--r--ospfd/ospf_asbr.c4
-rw-r--r--ospfd/ospf_ase.c10
-rw-r--r--ospfd/ospf_dump.c27
-rw-r--r--ospfd/ospf_dump.h5
-rw-r--r--ospfd/ospf_flood.c61
-rw-r--r--ospfd/ospf_flood.h3
-rw-r--r--ospfd/ospf_gr.c824
-rw-r--r--ospfd/ospf_gr.h (renamed from ospfd/ospf_gr_helper.h)20
-rw-r--r--ospfd/ospf_gr_helper.c69
-rw-r--r--ospfd/ospf_lsa.c90
-rw-r--r--ospfd/ospf_lsa.h1
-rw-r--r--ospfd/ospf_main.c2
-rw-r--r--ospfd/ospf_neighbor.c2
-rw-r--r--ospfd/ospf_neighbor.h2
-rw-r--r--ospfd/ospf_nsm.c6
-rw-r--r--ospfd/ospf_opaque.c5
-rw-r--r--ospfd/ospf_packet.c24
-rw-r--r--ospfd/ospf_spf.c2
-rw-r--r--ospfd/ospf_spf.h1
-rw-r--r--ospfd/ospf_vty.c28
-rw-r--r--ospfd/ospf_zebra.c76
-rw-r--r--ospfd/ospf_zebra.h2
-rw-r--r--ospfd/ospfd.c21
-rw-r--r--ospfd/ospfd.h13
-rw-r--r--ospfd/subdir.am7
-rw-r--r--pathd/path_pcep_cli.c6
-rw-r--r--pathd/subdir.am2
-rw-r--r--pimd/pim_cmd.c60
-rw-r--r--pimd/pim_iface.c13
-rw-r--r--pimd/pim_igmp.c69
-rw-r--r--pimd/pim_igmp.h6
-rw-r--r--pimd/pim_nb_config.c2
-rw-r--r--pimd/pim_neighbor.c8
-rw-r--r--pimd/pim_rp.c33
-rw-r--r--pimd/pim_sock.c18
-rw-r--r--qpb/subdir.am2
-rw-r--r--ripd/subdir.am2
-rw-r--r--staticd/static_routes.c7
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/lib/cli/common_cli.c9
-rw-r--r--tests/lib/cli/common_cli.h3
-rw-r--r--tests/lib/test_plist.c48
-rw-r--r--tests/subdir.am5
-rw-r--r--tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py32
-rw-r--r--tests/topotests/bgp_community_alias/r1/bgpd.conf13
-rw-r--r--tests/topotests/bgp_community_alias/r2/bgpd.conf3
-rw-r--r--tests/topotests/bgp_community_alias/r2/zebra.conf1
-rw-r--r--tests/topotests/bgp_community_alias/test_bgp-community-alias.py44
-rw-r--r--tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf1
-rw-r--r--tests/topotests/bgp_default_route/r2/bgpd.conf3
-rw-r--r--tests/topotests/bgp_default_route/test_bgp_default-originate.py27
-rwxr-xr-xtests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py8
-rwxr-xr-xtests/topotests/conftest.py23
-rwxr-xr-xtests/topotests/docker/frr-topotests.sh10
-rw-r--r--tests/topotests/evpn_pim_1/leaf1/pimd.conf1
-rw-r--r--tests/topotests/evpn_pim_1/leaf2/pimd.conf1
-rw-r--r--tests/topotests/evpn_pim_1/spine/pimd.conf1
-rw-r--r--tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json191
-rw-r--r--tests/topotests/lib/bgp.py412
-rw-r--r--tests/topotests/lib/common_config.py128
-rw-r--r--tests/topotests/lib/ospf.py810
-rw-r--r--tests/topotests/lib/topogen.py4
-rw-r--r--tests/topotests/lib/topotest.py63
-rw-r--r--tests/topotests/msdp_mesh_topo1/r1/pimd.conf1
-rw-r--r--tests/topotests/msdp_mesh_topo1/r2/pimd.conf1
-rw-r--r--tests/topotests/msdp_mesh_topo1/r3/pimd.conf1
-rw-r--r--tests/topotests/msdp_topo1/r1/pimd.conf1
-rw-r--r--tests/topotests/msdp_topo1/r2/pimd.conf1
-rw-r--r--tests/topotests/msdp_topo1/r3/pimd.conf1
-rw-r--r--tests/topotests/msdp_topo1/r4/pimd.conf1
-rw-r--r--tests/topotests/ospf_gr_topo1/__init__.py0
-rw-r--r--tests/topotests/ospf_gr_topo1/rt1/ospfd.conf32
-rw-r--r--tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_database.json98
-rw-r--r--tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json11
-rw-r--r--tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_route.json180
-rw-r--r--tests/topotests/ospf_gr_topo1/rt1/show_ip_route.json210
-rw-r--r--tests/topotests/ospf_gr_topo1/rt1/zebra.conf23
-rw-r--r--tests/topotests/ospf_gr_topo1/rt2/ospfd.conf37
-rw-r--r--tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_database.json160
-rw-r--r--tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json18
-rw-r--r--tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_route.json201
-rw-r--r--tests/topotests/ospf_gr_topo1/rt2/show_ip_route.json224
-rw-r--r--tests/topotests/ospf_gr_topo1/rt2/zebra.conf23
-rw-r--r--tests/topotests/ospf_gr_topo1/rt3/ospfd.conf43
-rw-r--r--tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_database.json83
-rw-r--r--tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json25
-rw-r--r--tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_route.json214
-rw-r--r--tests/topotests/ospf_gr_topo1/rt3/show_ip_route.json223
-rw-r--r--tests/topotests/ospf_gr_topo1/rt3/zebra.conf26
-rw-r--r--tests/topotests/ospf_gr_topo1/rt4/ospfd.conf37
-rw-r--r--tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_database.json164
-rw-r--r--tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json18
-rw-r--r--tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_route.json202
-rw-r--r--tests/topotests/ospf_gr_topo1/rt4/show_ip_route.json224
-rw-r--r--tests/topotests/ospf_gr_topo1/rt4/zebra.conf23
-rw-r--r--tests/topotests/ospf_gr_topo1/rt5/ospfd.conf31
-rw-r--r--tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_database.json102
-rw-r--r--tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json11
-rw-r--r--tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_route.json203
-rw-r--r--tests/topotests/ospf_gr_topo1/rt5/show_ip_route.json225
-rw-r--r--tests/topotests/ospf_gr_topo1/rt5/zebra.conf20
-rw-r--r--tests/topotests/ospf_gr_topo1/rt6/ospfd.conf38
-rw-r--r--tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_database.json168
-rw-r--r--tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json18
-rw-r--r--tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_route.json214
-rw-r--r--tests/topotests/ospf_gr_topo1/rt6/show_ip_route.json224
-rw-r--r--tests/topotests/ospf_gr_topo1/rt6/zebra.conf23
-rw-r--r--tests/topotests/ospf_gr_topo1/rt7/ospfd.conf33
-rw-r--r--tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_database.json99
-rw-r--r--tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json11
-rw-r--r--tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_route.json168
-rw-r--r--tests/topotests/ospf_gr_topo1/rt7/show_ip_route.json210
-rw-r--r--tests/topotests/ospf_gr_topo1/rt7/zebra.conf23
-rwxr-xr-xtests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py393
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json347
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_routemaps.json137
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py520
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py872
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py228
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py2
-rw-r--r--tests/topotests/pim_acl/h1/zebra.conf10
-rw-r--r--tests/topotests/pim_acl/h2/zebra.conf8
-rw-r--r--tests/topotests/pim_acl/r1/acl_1_pim_join.json21
-rw-r--r--tests/topotests/pim_acl/r1/acl_2_pim_join.json21
-rw-r--r--tests/topotests/pim_acl/r1/acl_3_pim_join.json21
-rw-r--r--tests/topotests/pim_acl/r1/acl_4_pim_join.json21
-rw-r--r--tests/topotests/pim_acl/r1/acl_5_pim_join.json21
-rw-r--r--tests/topotests/pim_acl/r1/acl_6_pim_join.json21
-rw-r--r--tests/topotests/pim_acl/r1/ospf_neighbor.json59
-rw-r--r--tests/topotests/pim_acl/r1/ospfd.conf16
-rw-r--r--tests/topotests/pim_acl/r1/pim_neighbor.json31
-rw-r--r--tests/topotests/pim_acl/r1/pimd.conf31
-rw-r--r--tests/topotests/pim_acl/r1/zebra.conf18
-rw-r--r--tests/topotests/pim_acl/r11/acl_1_pim_join.json19
-rw-r--r--tests/topotests/pim_acl/r11/ospfd.conf14
-rw-r--r--tests/topotests/pim_acl/r11/pimd.conf17
-rw-r--r--tests/topotests/pim_acl/r11/zebra.conf13
-rw-r--r--tests/topotests/pim_acl/r12/acl_2_pim_join.json19
-rw-r--r--tests/topotests/pim_acl/r12/ospfd.conf14
-rw-r--r--tests/topotests/pim_acl/r12/pimd.conf17
-rw-r--r--tests/topotests/pim_acl/r12/zebra.conf13
-rw-r--r--tests/topotests/pim_acl/r13/acl_3_pim_join.json19
-rw-r--r--tests/topotests/pim_acl/r13/ospfd.conf14
-rw-r--r--tests/topotests/pim_acl/r13/pimd.conf17
-rw-r--r--tests/topotests/pim_acl/r13/zebra.conf13
-rw-r--r--tests/topotests/pim_acl/r14/acl_4_pim_join.json19
-rw-r--r--tests/topotests/pim_acl/r14/acl_5_pim_join.json19
-rw-r--r--tests/topotests/pim_acl/r14/ospfd.conf14
-rw-r--r--tests/topotests/pim_acl/r14/pimd.conf18
-rw-r--r--tests/topotests/pim_acl/r14/zebra.conf13
-rw-r--r--tests/topotests/pim_acl/r15/acl_6_pim_join.json19
-rw-r--r--tests/topotests/pim_acl/r15/ospfd.conf14
-rw-r--r--tests/topotests/pim_acl/r15/pimd.conf17
-rw-r--r--tests/topotests/pim_acl/r15/zebra.conf13
-rwxr-xr-xtests/topotests/pim_acl/test_pim_acl.py418
-rw-r--r--tests/topotests/pim_basic/r1/pimd.conf1
-rw-r--r--tests/topotests/pim_basic/rp/pimd.conf1
-rw-r--r--tests/topotests/pim_basic_topo2/r2/pimd.conf1
-rw-r--r--tests/topotests/pim_igmp_vrf/h1/zebra.conf10
-rw-r--r--tests/topotests/pim_igmp_vrf/h2/zebra.conf8
-rw-r--r--tests/topotests/pim_igmp_vrf/h3/zebra.conf10
-rw-r--r--tests/topotests/pim_igmp_vrf/h4/zebra.conf8
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json15
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json16
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/ospfd.conf26
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pim_blue_join.json22
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pim_blue_neighbor.json13
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pim_blue_pimreg11.json14
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pim_red_join.json21
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pim_red_neighbor.json13
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pim_red_pimreg12.json14
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pimd.conf27
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/zebra.conf30
-rw-r--r--tests/topotests/pim_igmp_vrf/r11/ospfd.conf14
-rw-r--r--tests/topotests/pim_igmp_vrf/r11/pim_blue_join.json19
-rw-r--r--tests/topotests/pim_igmp_vrf/r11/pimd.conf17
-rw-r--r--tests/topotests/pim_igmp_vrf/r11/zebra.conf13
-rw-r--r--tests/topotests/pim_igmp_vrf/r12/ospfd.conf14
-rw-r--r--tests/topotests/pim_igmp_vrf/r12/pim_red_join.json19
-rw-r--r--tests/topotests/pim_igmp_vrf/r12/pimd.conf17
-rw-r--r--tests/topotests/pim_igmp_vrf/r12/zebra.conf13
-rwxr-xr-xtests/topotests/pim_igmp_vrf/test_pim_vrf.py462
-rw-r--r--tools/coccinelle/hash_compare_null_values_check.cocci20
-rwxr-xr-xtools/frr-reload.py46
-rw-r--r--vtysh/vtysh.c44
-rw-r--r--yang/frr-bgp-route-map.yang15
-rw-r--r--yang/frr-pim.yang6
-rw-r--r--zebra/if_netlink.c32
-rw-r--r--zebra/kernel_socket.c12
-rw-r--r--zebra/redistribute.c6
-rw-r--r--zebra/rib.h45
-rw-r--r--zebra/rt.h4
-rw-r--r--zebra/rt_netlink.c14
-rw-r--r--zebra/rt_netlink.h5
-rw-r--r--zebra/rtread_netlink.c6
-rw-r--r--zebra/rtread_sysctl.c4
-rw-r--r--zebra/subdir.am12
-rw-r--r--zebra/zapi_msg.c20
-rw-r--r--zebra/zebra_dplane.c10
-rw-r--r--zebra/zebra_evpn.c29
-rw-r--r--zebra/zebra_evpn.h10
-rw-r--r--zebra/zebra_evpn_mac.c44
-rw-r--r--zebra/zebra_evpn_mac.h42
-rw-r--r--zebra/zebra_evpn_mh.c23
-rw-r--r--zebra/zebra_evpn_mh.h9
-rw-r--r--zebra/zebra_evpn_neigh.c44
-rw-r--r--zebra/zebra_evpn_neigh.h36
-rw-r--r--zebra/zebra_mpls.c34
-rw-r--r--zebra/zebra_mpls.h6
-rw-r--r--zebra/zebra_nhg.c9
-rw-r--r--zebra/zebra_rib.c403
-rw-r--r--zebra/zebra_rnh.c3
-rw-r--r--zebra/zebra_rnh.h3
-rw-r--r--zebra/zebra_vrf.h1
-rw-r--r--zebra/zebra_vxlan.c282
-rw-r--r--zebra/zebra_vxlan.h12
-rw-r--r--zebra/zserv.c2
289 files changed, 13097 insertions, 1755 deletions
diff --git a/Makefile.am b/Makefile.am
index 9bc5dd7d22..ce0f70a1a2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -29,11 +29,33 @@ AM_CPPFLAGS = \
-I$(top_srcdir)/lib/assert \
$(CPPFLAGS_BASE) \
# end
+
+# AM_LDFLAGS is used for executables (daemons). LDFLAGS can be left alone,
+# but if it is changed it should include $(AM_LDFLAGS)
AM_LDFLAGS = \
-export-dynamic \
$(AC_LDFLAGS) \
+ $(AC_LDFLAGS_EXEC) \
+ $(SAN_FLAGS) \
+ # end
+
+# libraries need to use libxxx_LDFLAGS = $(LIB_LDFLAGS) -version-info X:Y:Z
+LIB_LDFLAGS = \
+ -export-dynamic \
+ $(AC_LDFLAGS) \
$(SAN_FLAGS) \
# end
+
+# modules need to use xxx_LDFLAGS = $(MODULE_LDFLAGS)
+MODULE_LDFLAGS = \
+ -export-dynamic \
+ -avoid-version \
+ -module \
+ -shared \
+ $(AC_LDFLAGS) \
+ $(SAN_FLAGS) \
+ # end
+
DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" -DCONFDATE=$(CONFDATE)
AR_FLAGS = @AR_FLAGS@
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 24b48178d2..adf408220e 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -2915,11 +2915,8 @@ static bgp_attr_parse_ret_t bgp_attr_unknown(struct bgp_attr_parser_args *args)
if (!transit)
transit = XCALLOC(MTYPE_TRANSIT, sizeof(struct transit));
- if (transit->val)
- transit->val = XREALLOC(MTYPE_TRANSIT_VAL, transit->val,
- transit->length + total);
- else
- transit->val = XMALLOC(MTYPE_TRANSIT_VAL, total);
+ transit->val = XREALLOC(MTYPE_TRANSIT_VAL, transit->val,
+ transit->length + total);
memcpy(transit->val + transit->length, startp, total);
transit->length += total;
diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c
index f6f2f5f6e4..4995f9a1fd 100644
--- a/bgpd/bgp_bfd.c
+++ b/bgpd/bgp_bfd.c
@@ -326,7 +326,9 @@ static void bgp_peer_remove_bfd(struct peer *p)
return;
}
- bfd_sess_free(&p->bfd_config->session);
+ if (p->bfd_config)
+ bfd_sess_free(&p->bfd_config->session);
+
XFREE(MTYPE_BFD_CONFIG, p->bfd_config);
}
diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c
index 2aa6a56a5e..e91166449a 100644
--- a/bgpd/bgp_community.c
+++ b/bgpd/bgp_community.c
@@ -60,11 +60,7 @@ void community_free(struct community **com)
void community_add_val(struct community *com, uint32_t val)
{
com->size++;
- if (com->val)
- com->val = XREALLOC(MTYPE_COMMUNITY_VAL, com->val,
- com_length(com));
- else
- com->val = XMALLOC(MTYPE_COMMUNITY_VAL, com_length(com));
+ com->val = XREALLOC(MTYPE_COMMUNITY_VAL, com->val, com_length(com));
val = htonl(val);
memcpy(com_lastval(com), &val, sizeof(uint32_t));
@@ -618,13 +614,8 @@ bool community_cmp(const struct community *com1, const struct community *com2)
struct community *community_merge(struct community *com1,
struct community *com2)
{
- if (com1->val)
- com1->val =
- XREALLOC(MTYPE_COMMUNITY_VAL, com1->val,
- (com1->size + com2->size) * COMMUNITY_SIZE);
- else
- com1->val = XMALLOC(MTYPE_COMMUNITY_VAL,
- (com1->size + com2->size) * COMMUNITY_SIZE);
+ com1->val = XREALLOC(MTYPE_COMMUNITY_VAL, com1->val,
+ (com1->size + com2->size) * COMMUNITY_SIZE);
memcpy(com1->val + com1->size, com2->val, com2->size * COMMUNITY_SIZE);
com1->size += com2->size;
diff --git a/bgpd/bgp_community_alias.c b/bgpd/bgp_community_alias.c
index 6e510a0a04..f770ebdd5d 100644
--- a/bgpd/bgp_community_alias.c
+++ b/bgpd/bgp_community_alias.c
@@ -152,3 +152,32 @@ const char *bgp_community2alias(char *community)
return community;
}
+
+static int bgp_community_alias_vector_walker(struct hash_bucket *bucket,
+ void *data)
+{
+ vector *comps = data;
+ struct community_alias *alias = bucket->data;
+
+ vector_set(*comps, XSTRDUP(MTYPE_COMPLETION, alias->alias));
+
+ return 1;
+}
+
+static void bgp_community_alias_cmd_completion(vector comps,
+ struct cmd_token *token)
+{
+ hash_walk(bgp_ca_alias_hash, bgp_community_alias_vector_walker, &comps);
+}
+
+static const struct cmd_variable_handler community_alias_handlers[] = {
+ {.varname = "alias_name",
+ .completions = bgp_community_alias_cmd_completion},
+ {.tokenname = "ALIAS_NAME",
+ .completions = bgp_community_alias_cmd_completion},
+ {.completions = NULL}};
+
+void bgp_community_alias_command_completion_setup(void)
+{
+ cmd_variable_handler_register(community_alias_handlers);
+}
diff --git a/bgpd/bgp_community_alias.h b/bgpd/bgp_community_alias.h
index c84119a0d6..ab8ed06ee6 100644
--- a/bgpd/bgp_community_alias.h
+++ b/bgpd/bgp_community_alias.h
@@ -42,5 +42,6 @@ extern void bgp_ca_community_delete(struct community_alias *ca);
extern void bgp_ca_alias_delete(struct community_alias *ca);
extern int bgp_community_alias_write(struct vty *vty);
extern const char *bgp_community2alias(char *community);
+extern void bgp_community_alias_command_completion_setup(void);
#endif /* FRR_BGP_COMMUNITY_ALIAS_H */
diff --git a/bgpd/bgp_conditional_adv.c b/bgpd/bgp_conditional_adv.c
index 49bc38be66..329bd3d696 100644
--- a/bgpd/bgp_conditional_adv.c
+++ b/bgpd/bgp_conditional_adv.c
@@ -184,7 +184,7 @@ static int bgp_conditional_adv_timer(struct thread *t)
assert(bgp);
thread_add_timer(bm->master, bgp_conditional_adv_timer, bgp,
- CONDITIONAL_ROUTES_POLL_TIME, &bgp->t_condition_check);
+ bgp->condition_check_period, &bgp->t_condition_check);
/* loop through each peer and advertise or withdraw routes if
* advertise-map is configured and prefix(es) in condition-map
@@ -315,7 +315,7 @@ void bgp_conditional_adv_enable(struct peer *peer, afi_t afi, safi_t safi)
/* Register for conditional routes polling timer */
thread_add_timer(bm->master, bgp_conditional_adv_timer, bgp,
- CONDITIONAL_ROUTES_POLL_TIME, &bgp->t_condition_check);
+ bgp->condition_check_period, &bgp->t_condition_check);
}
void bgp_conditional_adv_disable(struct peer *peer, afi_t afi, safi_t safi)
diff --git a/bgpd/bgp_conditional_adv.h b/bgpd/bgp_conditional_adv.h
index 7b5053de76..371ae856c2 100644
--- a/bgpd/bgp_conditional_adv.h
+++ b/bgpd/bgp_conditional_adv.h
@@ -34,7 +34,7 @@ extern "C" {
#endif
/* Polling time for monitoring condition-map routes in route table */
-#define CONDITIONAL_ROUTES_POLL_TIME 60
+#define DEFAULT_CONDITIONAL_ROUTES_POLL_TIME 60
extern void bgp_conditional_adv_enable(struct peer *peer, afi_t afi,
safi_t safi);
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
index 923c9b0d7e..3a951e6e80 100644
--- a/bgpd/bgp_ecommunity.c
+++ b/bgpd/bgp_ecommunity.c
@@ -158,7 +158,6 @@ static bool ecommunity_add_val_internal(struct ecommunity *ecom,
ecom->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom->val,
ecom_length_size(ecom, ecom_size));
-
memmove(ecom->val + ((ins_idx + 1) * ecom_size),
ecom->val + (ins_idx * ecom_size),
(ecom->size - 1 - ins_idx) * ecom_size);
@@ -287,14 +286,9 @@ char *ecommunity_str(struct ecommunity *ecom)
struct ecommunity *ecommunity_merge(struct ecommunity *ecom1,
struct ecommunity *ecom2)
{
- if (ecom1->val)
- ecom1->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val,
- (size_t)(ecom1->size + ecom2->size)
- * (size_t)ecom1->unit_size);
- else
- ecom1->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
- (size_t)(ecom1->size + ecom2->size)
- * (size_t)ecom1->unit_size);
+ ecom1->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val,
+ (size_t)(ecom1->size + ecom2->size)
+ * (size_t)ecom1->unit_size);
memcpy(ecom1->val + (ecom1->size * ecom1->unit_size), ecom2->val,
(size_t)ecom2->size * (size_t)ecom1->unit_size);
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
index c99f539c7b..88581736a3 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -111,11 +111,7 @@ static bool vni_hash_cmp(const void *p1, const void *p2)
const struct bgpevpn *vpn1 = p1;
const struct bgpevpn *vpn2 = p2;
- if (!vpn1 && !vpn2)
- return true;
- if (!vpn1 || !vpn2)
- return false;
- return (vpn1->vni == vpn2->vni);
+ return vpn1->vni == vpn2->vni;
}
int vni_list_cmp(void *p1, void *p2)
@@ -145,12 +141,6 @@ static bool vrf_import_rt_hash_cmp(const void *p1, const void *p2)
const struct vrf_irt_node *irt1 = p1;
const struct vrf_irt_node *irt2 = p2;
- if (irt1 == NULL && irt2 == NULL)
- return true;
-
- if (irt1 == NULL || irt2 == NULL)
- return false;
-
return (memcmp(irt1->rt.val, irt2->rt.val, ECOMMUNITY_SIZE) == 0);
}
@@ -261,12 +251,6 @@ static bool import_rt_hash_cmp(const void *p1, const void *p2)
const struct irt_node *irt1 = p1;
const struct irt_node *irt2 = p2;
- if (irt1 == NULL && irt2 == NULL)
- return true;
-
- if (irt1 == NULL || irt2 == NULL)
- return false;
-
return (memcmp(irt1->rt.val, irt2->rt.val, ECOMMUNITY_SIZE) == 0);
}
@@ -2536,7 +2520,7 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
/* Gateway IP nexthop should be resolved */
if (attr.evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) {
if (bgp_find_or_add_nexthop(bgp_vrf, bgp_vrf, afi, safi, pi,
- NULL, 0))
+ NULL, 0, NULL))
bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID);
else {
if (BGP_DEBUG(nht, NHT)) {
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index 79dc0aec9d..8d996e16eb 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -110,9 +110,9 @@ int bgp_peer_reg_with_nht(struct peer *peer)
&& !CHECK_FLAG(peer->bgp->flags, BGP_FLAG_DISABLE_NH_CONNECTED_CHK))
connected = 1;
- return bgp_find_or_add_nexthop(peer->bgp, peer->bgp,
- family2afi(peer->su.sa.sa_family),
- SAFI_UNICAST, NULL, peer, connected);
+ return bgp_find_or_add_nexthop(
+ peer->bgp, peer->bgp, family2afi(peer->su.sa.sa_family),
+ SAFI_UNICAST, NULL, peer, connected, NULL);
}
static void peer_xfer_stats(struct peer *peer_dst, struct peer *peer_src)
@@ -258,10 +258,6 @@ static struct peer *peer_xfer_conn(struct peer *from_peer)
peer->last_reset = from_peer->last_reset;
peer->max_packet_size = from_peer->max_packet_size;
- peer->peer_gr_present_state = from_peer->peer_gr_present_state;
- peer->peer_gr_new_status_flag = from_peer->peer_gr_new_status_flag;
- bgp_peer_gr_flags_update(peer);
-
BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(peer->bgp,
peer->bgp->peer);
diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c
index fa4d4aee28..6121c4905f 100644
--- a/bgpd/bgp_lcommunity.c
+++ b/bgpd/bgp_lcommunity.c
@@ -166,12 +166,8 @@ struct lcommunity *lcommunity_dup(struct lcommunity *lcom)
struct lcommunity *lcommunity_merge(struct lcommunity *lcom1,
struct lcommunity *lcom2)
{
- if (lcom1->val)
- lcom1->val = XREALLOC(MTYPE_LCOMMUNITY_VAL, lcom1->val,
- lcom_length(lcom1) + lcom_length(lcom2));
- else
- lcom1->val = XMALLOC(MTYPE_LCOMMUNITY_VAL,
- lcom_length(lcom1) + lcom_length(lcom2));
+ lcom1->val = XREALLOC(MTYPE_LCOMMUNITY_VAL, lcom1->val,
+ lcom_length(lcom1) + lcom_length(lcom2));
memcpy(lcom1->val + lcom_length(lcom1), lcom2->val, lcom_length(lcom2));
lcom1->size += lcom2->size;
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index 0de48dcf78..cab58b45b8 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -845,7 +845,7 @@ leak_update(struct bgp *bgp, /* destination bgp instance */
* 'connected' parameter?
*/
nh_valid = bgp_find_or_add_nexthop(
- bgp, bgp_nexthop, afi, safi, bpi, NULL, 0);
+ bgp, bgp_nexthop, afi, safi, bpi, NULL, 0, p);
if (debug)
zlog_debug("%s: nexthop is %svalid (in vrf %s)",
@@ -931,7 +931,7 @@ leak_update(struct bgp *bgp, /* destination bgp instance */
* 'connected' parameter?
*/
nh_valid = bgp_find_or_add_nexthop(bgp, bgp_nexthop, afi, safi,
- new, NULL, 0);
+ new, NULL, 0, p);
if (debug)
zlog_debug("%s: nexthop is %svalid (in vrf %s)",
diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c
index eb00a4641c..c77e240855 100644
--- a/bgpd/bgp_nht.c
+++ b/bgpd/bgp_nht.c
@@ -160,7 +160,8 @@ void bgp_unlink_nexthop_by_peer(struct peer *peer)
*/
int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop,
afi_t afi, safi_t safi, struct bgp_path_info *pi,
- struct peer *peer, int connected)
+ struct peer *peer, int connected,
+ const struct prefix *orig_prefix)
{
struct bgp_nexthop_cache_head *tree = NULL;
struct bgp_nexthop_cache *bnc;
@@ -192,6 +193,16 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop,
if (make_prefix(afi, pi, &p) < 0)
return 1;
+ if (!is_bgp_static_route && orig_prefix
+ && prefix_same(&p, orig_prefix)) {
+ if (BGP_DEBUG(nht, NHT)) {
+ zlog_debug(
+ "%s(%pFX): prefix loops through itself",
+ __func__, &p);
+ }
+ return 0;
+ }
+
srte_color = pi->attr->srte_color;
} else if (peer) {
/*
diff --git a/bgpd/bgp_nht.h b/bgpd/bgp_nht.h
index e006aa4469..43f0aa26cf 100644
--- a/bgpd/bgp_nht.h
+++ b/bgpd/bgp_nht.h
@@ -42,7 +42,8 @@ extern void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id);
extern int bgp_find_or_add_nexthop(struct bgp *bgp_route,
struct bgp *bgp_nexthop, afi_t a,
safi_t safi, struct bgp_path_info *p,
- struct peer *peer, int connected);
+ struct peer *peer, int connected,
+ const struct prefix *orig_prefix);
/**
* bgp_unlink_nexthop() - Unlink the nexthop object from the path structure.
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 107aa1eb12..66ff16d53a 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -3663,7 +3663,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
if (aspath_loop_check(attr->aspath, peer->change_local_as)
> aspath_loop_count) {
peer->stat_pfx_aspath_loop++;
- reason = "as-path contains our own AS A;";
+ reason = "as-path contains our own AS;";
goto filtered;
}
}
@@ -4103,7 +4103,8 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
nh_afi = BGP_ATTR_NH_AFI(afi, pi->attr);
if (bgp_find_or_add_nexthop(bgp, bgp_nexthop, nh_afi,
- safi, pi, NULL, connected)
+ safi, pi, NULL, connected,
+ p)
|| CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD))
bgp_path_info_set_flag(dest, pi,
BGP_PATH_VALID);
@@ -4244,7 +4245,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
nh_afi = BGP_ATTR_NH_AFI(afi, new->attr);
if (bgp_find_or_add_nexthop(bgp, bgp, nh_afi, safi, new, NULL,
- connected)
+ connected, p)
|| CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD))
bgp_path_info_set_flag(dest, new, BGP_PATH_VALID);
else {
@@ -5683,7 +5684,7 @@ void bgp_static_update(struct bgp *bgp, const struct prefix *p,
if (bgp_find_or_add_nexthop(bgp, bgp_nexthop,
afi, safi, pi, NULL,
- 0))
+ 0, p))
bgp_path_info_set_flag(dest, pi,
BGP_PATH_VALID);
else {
@@ -5735,7 +5736,8 @@ void bgp_static_update(struct bgp *bgp, const struct prefix *p,
/* Nexthop reachability check. */
if (CHECK_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK)
&& (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST)) {
- if (bgp_find_or_add_nexthop(bgp, bgp, afi, safi, new, NULL, 0))
+ if (bgp_find_or_add_nexthop(bgp, bgp, afi, safi, new, NULL, 0,
+ p))
bgp_path_info_set_flag(dest, new, BGP_PATH_VALID);
else {
if (BGP_DEBUG(nht, NHT)) {
@@ -6052,6 +6054,7 @@ int bgp_static_set(struct bgp *bgp, const char *negate, struct prefix *pfx,
&& (label_index != bgp_static->label_index)) {
snprintf(errmsg, errmsg_len,
"label-index doesn't match static route\n");
+ bgp_dest_unlock_node(dest);
return -1;
}
@@ -6059,6 +6062,7 @@ int bgp_static_set(struct bgp *bgp, const char *negate, struct prefix *pfx,
&& strcmp(rmap, bgp_static->rmap.name)) {
snprintf(errmsg, errmsg_len,
"route-map name doesn't match static route\n");
+ bgp_dest_unlock_node(dest);
return -1;
}
@@ -9517,14 +9521,18 @@ void route_vty_out_overlay(struct vty *vty, const struct prefix *p,
static void damp_route_vty_out(struct vty *vty, const struct prefix *p,
struct bgp_path_info *path, int display,
afi_t afi, safi_t safi, bool use_json,
- json_object *json)
+ json_object *json_paths)
{
- struct attr *attr;
+ struct attr *attr = path->attr;
int len;
char timebuf[BGP_UPTIME_LEN];
+ json_object *json_path = NULL;
+
+ if (use_json)
+ json_path = json_object_new_object();
/* short status lead text */
- route_vty_short_status_out(vty, path, p, json);
+ route_vty_short_status_out(vty, path, p, json_path);
/* print prefix and mask */
if (!use_json) {
@@ -9532,147 +9540,125 @@ static void damp_route_vty_out(struct vty *vty, const struct prefix *p,
route_vty_out_route(path->net, p, vty, NULL, false);
else
vty_out(vty, "%*s", 17, " ");
- }
- len = vty_out(vty, "%s", path->peer->host);
- len = 17 - len;
- if (len < 1) {
- if (!use_json)
+ len = vty_out(vty, "%s", path->peer->host);
+ len = 17 - len;
+
+ if (len < 1)
vty_out(vty, "\n%*s", 34, " ");
- } else {
- if (use_json)
- json_object_int_add(json, "peerHost", len);
else
vty_out(vty, "%*s", len, " ");
- }
- if (use_json)
- bgp_damp_reuse_time_vty(vty, path, timebuf, BGP_UPTIME_LEN, afi,
- safi, use_json, json);
- else
vty_out(vty, "%s ",
bgp_damp_reuse_time_vty(vty, path, timebuf,
BGP_UPTIME_LEN, afi, safi,
- use_json, json));
+ use_json, NULL));
- /* Print attribute */
- attr = path->attr;
-
- /* Print aspath */
- if (attr->aspath) {
- if (use_json)
- json_object_string_add(json, "asPath",
- attr->aspath->str);
- else
+ if (attr->aspath)
aspath_print_vty(vty, "%s", attr->aspath, " ");
- }
- /* Print origin */
- if (use_json)
- json_object_string_add(json, "origin",
- bgp_origin_str[attr->origin]);
- else
vty_out(vty, "%s", bgp_origin_str[attr->origin]);
- if (!use_json)
vty_out(vty, "\n");
+ } else {
+ bgp_damp_reuse_time_vty(vty, path, timebuf, BGP_UPTIME_LEN, afi,
+ safi, use_json, json_path);
+
+ if (attr->aspath)
+ json_object_string_add(json_path, "asPath",
+ attr->aspath->str);
+
+ json_object_string_add(json_path, "origin",
+ bgp_origin_str[attr->origin]);
+ json_object_string_add(json_path, "peerHost", path->peer->host);
+
+ json_object_array_add(json_paths, json_path);
+ }
}
/* flap route */
static void flap_route_vty_out(struct vty *vty, const struct prefix *p,
struct bgp_path_info *path, int display,
afi_t afi, safi_t safi, bool use_json,
- json_object *json)
+ json_object *json_paths)
{
- struct attr *attr;
+ struct attr *attr = path->attr;
struct bgp_damp_info *bdi;
char timebuf[BGP_UPTIME_LEN];
int len;
+ json_object *json_path = NULL;
if (!path->extra)
return;
+ if (use_json)
+ json_path = json_object_new_object();
+
bdi = path->extra->damp_info;
/* short status lead text */
- route_vty_short_status_out(vty, path, p, json);
+ route_vty_short_status_out(vty, path, p, json_path);
- /* print prefix and mask */
if (!use_json) {
if (!display)
route_vty_out_route(path->net, p, vty, NULL, false);
else
vty_out(vty, "%*s", 17, " ");
- }
- len = vty_out(vty, "%s", path->peer->host);
- len = 16 - len;
- if (len < 1) {
- if (!use_json)
+ len = vty_out(vty, "%s", path->peer->host);
+ len = 16 - len;
+ if (len < 1)
vty_out(vty, "\n%*s", 33, " ");
- } else {
- if (use_json)
- json_object_int_add(json, "peerHost", len);
else
vty_out(vty, "%*s", len, " ");
- }
- len = vty_out(vty, "%d", bdi->flap);
- len = 5 - len;
- if (len < 1) {
- if (!use_json)
+ len = vty_out(vty, "%d", bdi->flap);
+ len = 5 - len;
+ if (len < 1)
vty_out(vty, " ");
- } else {
- if (use_json)
- json_object_int_add(json, "bdiFlap", len);
else
vty_out(vty, "%*s", len, " ");
- }
- if (use_json)
- peer_uptime(bdi->start_time, timebuf, BGP_UPTIME_LEN, use_json,
- json);
- else
vty_out(vty, "%s ", peer_uptime(bdi->start_time, timebuf,
BGP_UPTIME_LEN, 0, NULL));
- if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED)
- && !CHECK_FLAG(path->flags, BGP_PATH_HISTORY)) {
- if (use_json)
- bgp_damp_reuse_time_vty(vty, path, timebuf,
- BGP_UPTIME_LEN, afi, safi,
- use_json, json);
- else
+ if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED)
+ && !CHECK_FLAG(path->flags, BGP_PATH_HISTORY))
vty_out(vty, "%s ",
bgp_damp_reuse_time_vty(vty, path, timebuf,
BGP_UPTIME_LEN, afi,
- safi, use_json, json));
- } else {
- if (!use_json)
+ safi, use_json, NULL));
+ else
vty_out(vty, "%*s ", 8, " ");
- }
-
- /* Print attribute */
- attr = path->attr;
- /* Print aspath */
- if (attr->aspath) {
- if (use_json)
- json_object_string_add(json, "asPath",
- attr->aspath->str);
- else
+ if (attr->aspath)
aspath_print_vty(vty, "%s", attr->aspath, " ");
- }
- /* Print origin */
- if (use_json)
- json_object_string_add(json, "origin",
- bgp_origin_str[attr->origin]);
- else
vty_out(vty, "%s", bgp_origin_str[attr->origin]);
- if (!use_json)
vty_out(vty, "\n");
+ } else {
+ json_object_string_add(json_path, "peerHost", path->peer->host);
+ json_object_int_add(json_path, "bdiFlap", bdi->flap);
+
+ peer_uptime(bdi->start_time, timebuf, BGP_UPTIME_LEN, use_json,
+ json_path);
+
+ if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED)
+ && !CHECK_FLAG(path->flags, BGP_PATH_HISTORY))
+ bgp_damp_reuse_time_vty(vty, path, timebuf,
+ BGP_UPTIME_LEN, afi, safi,
+ use_json, json_path);
+
+ if (attr->aspath)
+ json_object_string_add(json_path, "asPath",
+ attr->aspath->str);
+
+ json_object_string_add(json_path, "origin",
+ bgp_origin_str[attr->origin]);
+
+ json_object_array_add(json_paths, json_path);
+ }
}
static void route_vty_out_advertised_to(struct vty *vty, struct peer *peer,
@@ -10876,8 +10862,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
const char *com2alias =
bgp_community2alias(
communities[i]);
- if (strncmp(alias, com2alias,
- strlen(com2alias))
+ if (strcmp(alias, com2alias)
== 0) {
found = true;
break;
@@ -10892,8 +10877,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
const char *com2alias =
bgp_community2alias(
communities[i]);
- if (strncmp(alias, com2alias,
- strlen(com2alias))
+ if (strcmp(alias, com2alias)
== 0) {
found = true;
break;
@@ -12194,7 +12178,7 @@ DEFPY(show_ip_bgp_json, show_ip_bgp_json_cmd,
|route-filter-translated-v4] [exact-match]\
|rpki <invalid|valid|notfound>\
|version (1-4294967295)\
- |alias WORD\
+ |alias ALIAS_NAME\
] [json$uj [detail$detail] | wide$wide]",
SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR
BGP_SAFI_WITH_LABEL_HELP_STR
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 529abcbea0..61f57d0475 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -52,6 +52,7 @@
#include "bgpd/bgp_zebra.h"
#include "bgpd/bgp_regex.h"
#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_community_alias.h"
#include "bgpd/bgp_clist.h"
#include "bgpd/bgp_filter.h"
#include "bgpd/bgp_mplsvpn.h"
@@ -1179,6 +1180,55 @@ static const struct route_map_rule_cmd route_match_vrl_source_vrf_cmd = {
route_match_vrl_source_vrf_free
};
+/* `match alias` */
+static enum route_map_cmd_result_t
+route_match_alias(void *rule, const struct prefix *prefix, void *object)
+{
+ char *alias = rule;
+ struct bgp_path_info *path = object;
+ char **communities;
+ int num;
+
+ if (path->attr->community) {
+ frrstr_split(path->attr->community->str, " ", &communities,
+ &num);
+ for (int i = 0; i < num; i++) {
+ const char *com2alias =
+ bgp_community2alias(communities[i]);
+ if (strcmp(alias, com2alias) == 0)
+ return RMAP_MATCH;
+ }
+ }
+
+ if (path->attr->lcommunity) {
+ frrstr_split(path->attr->lcommunity->str, " ", &communities,
+ &num);
+ for (int i = 0; i < num; i++) {
+ const char *com2alias =
+ bgp_community2alias(communities[i]);
+ if (strcmp(alias, com2alias) == 0)
+ return RMAP_MATCH;
+ }
+ }
+
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_alias_compile(const char *arg)
+{
+
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void route_match_alias_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd route_match_alias_cmd = {
+ "alias", route_match_alias, route_match_alias_compile,
+ route_match_alias_free};
+
/* `match local-preference LOCAL-PREF' */
/* Match function return 1 if match is success else return zero. */
@@ -4597,6 +4647,58 @@ DEFUN_YANG (no_match_local_pref,
return nb_cli_apply_changes(vty, NULL);
}
+DEFUN_YANG(match_alias, match_alias_cmd, "match alias ALIAS_NAME",
+ MATCH_STR
+ "Match BGP community alias name\n"
+ "BGP community alias name\n")
+{
+ const char *alias = argv[2]->arg;
+ struct community_alias ca1;
+ struct community_alias *lookup_alias;
+
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:match-alias']";
+ char xpath_value[XPATH_MAXLEN];
+
+ memset(&ca1, 0, sizeof(ca1));
+ strlcpy(ca1.alias, alias, sizeof(ca1.alias));
+ lookup_alias = bgp_ca_alias_lookup(&ca1);
+ if (!lookup_alias) {
+ vty_out(vty, "%% BGP alias name '%s' does not exist\n", alias);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:alias", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, alias);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+
+DEFUN_YANG(no_match_alias, no_match_alias_cmd, "no match alias [ALIAS_NAME]",
+ NO_STR MATCH_STR
+ "Match BGP community alias name\n"
+ "BGP community alias name\n")
+{
+ int idx_alias = 3;
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:match-alias']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ if (argc <= idx_alias)
+ return nb_cli_apply_changes(vty, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:alias", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY,
+ argv[idx_alias]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
DEFPY_YANG (match_community,
match_community_cmd,
@@ -6286,6 +6388,7 @@ void bgp_route_map_init(void)
route_map_no_set_tag_hook(generic_set_delete);
route_map_install_match(&route_match_peer_cmd);
+ route_map_install_match(&route_match_alias_cmd);
route_map_install_match(&route_match_local_pref_cmd);
#ifdef HAVE_SCRIPTING
route_map_install_match(&route_match_script_cmd);
@@ -6370,6 +6473,8 @@ void bgp_route_map_init(void)
install_element(RMAP_NODE, &no_match_aspath_cmd);
install_element(RMAP_NODE, &match_local_pref_cmd);
install_element(RMAP_NODE, &no_match_local_pref_cmd);
+ install_element(RMAP_NODE, &match_alias_cmd);
+ install_element(RMAP_NODE, &no_match_alias_cmd);
install_element(RMAP_NODE, &match_community_cmd);
install_element(RMAP_NODE, &no_match_community_cmd);
install_element(RMAP_NODE, &match_lcommunity_cmd);
diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c
index 1254591b87..9216426968 100644
--- a/bgpd/bgp_routemap_nb.c
+++ b/bgpd/bgp_routemap_nb.c
@@ -39,6 +39,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = {
}
},
{
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:alias",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_alias_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_alias_destroy,
+ }
+ },
+ {
.xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:script",
.cbs = {
.modify = lib_route_map_entry_match_condition_rmap_match_condition_script_modify,
diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h
index f0e492eb61..069345b1a4 100644
--- a/bgpd/bgp_routemap_nb.h
+++ b/bgpd/bgp_routemap_nb.h
@@ -29,6 +29,10 @@ extern const struct frr_yang_module_info frr_bgp_route_map_info;
/* prototypes */
int lib_route_map_entry_match_condition_rmap_match_condition_local_preference_modify(struct nb_cb_modify_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_local_preference_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_alias_modify(
+ struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_alias_destroy(
+ struct nb_cb_destroy_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_script_modify(struct nb_cb_modify_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_script_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_origin_modify(struct nb_cb_modify_args *args);
diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c
index 88e3f6438f..45f5c8f4bc 100644
--- a/bgpd/bgp_routemap_nb_config.c
+++ b/bgpd/bgp_routemap_nb_config.c
@@ -160,6 +160,62 @@ lib_route_map_entry_match_condition_rmap_match_condition_local_preference_destro
}
/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:alias
+ */
+int lib_route_map_entry_match_condition_rmap_match_condition_alias_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *alias;
+ enum rmap_compile_rets ret;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ alias = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "alias";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, "alias", alias,
+ RMAP_EVENT_MATCH_ADDED, args->errmsg,
+ args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_VALIDATION;
+ }
+
+ break;
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_match_condition_rmap_match_condition_alias_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
* XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:script
*/
int
diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c
index 816ed88eec..a8bccecacf 100644
--- a/bgpd/bgp_rpki.c
+++ b/bgpd/bgp_rpki.c
@@ -908,7 +908,7 @@ static int config_write(struct vty *vty)
vty_out(vty, "!\n");
vty_out(vty, "rpki\n");
- vty_out(vty, " rpki polling_period %d\n", polling_period);
+ vty_out(vty, " rpki polling_period %d\n", polling_period);
for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
switch (cache->type) {
struct tr_tcp_config *tcp_config;
@@ -917,13 +917,13 @@ static int config_write(struct vty *vty)
#endif
case TCP:
tcp_config = cache->tr_config.tcp_config;
- vty_out(vty, " rpki cache %s %s ", tcp_config->host,
+ vty_out(vty, " rpki cache %s %s ", tcp_config->host,
tcp_config->port);
break;
#if defined(FOUND_SSH)
case SSH:
ssh_config = cache->tr_config.ssh_config;
- vty_out(vty, " rpki cache %s %u %s %s %s ",
+ vty_out(vty, " rpki cache %s %u %s %s %s ",
ssh_config->host, ssh_config->port,
ssh_config->username,
ssh_config->client_privkey_path,
@@ -938,7 +938,7 @@ static int config_write(struct vty *vty)
vty_out(vty, "preference %hhu\n", cache->preference);
}
- vty_out(vty, " exit\n");
+ vty_out(vty, " exit\n");
return 1;
}
diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c
index 038ef4f798..9c32c7ed1e 100644
--- a/bgpd/bgp_updgrp_packet.c
+++ b/bgpd/bgp_updgrp_packet.c
@@ -1158,6 +1158,7 @@ void subgroup_default_update_packet(struct update_subgroup *subgrp,
(void)bpacket_queue_add(SUBGRP_PKTQ(subgrp), s, &vecarr);
subgroup_trigger_write(subgrp);
+ subgrp->scount++;
}
void subgroup_default_withdraw_packet(struct update_subgroup *subgrp)
@@ -1250,6 +1251,7 @@ void subgroup_default_withdraw_packet(struct update_subgroup *subgrp)
(void)bpacket_queue_add(SUBGRP_PKTQ(subgrp), s, NULL);
subgroup_trigger_write(subgrp);
+ subgrp->scount--;
}
static void
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 9d7e88bb1c..dfdfab79a5 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -72,6 +72,7 @@
#include "bgpd/bgp_addpath.h"
#include "bgpd/bgp_mac.h"
#include "bgpd/bgp_flowspec.h"
+#include "bgpd/bgp_conditional_adv.h"
#ifdef ENABLE_BGP_VNC
#include "bgpd/rfapi/bgp_rfapi_cfg.h"
#endif
@@ -1564,7 +1565,7 @@ void cli_show_router_bgp_router_id(struct vty *vty, struct lyd_node *dnode,
}
DEFPY(bgp_community_alias, bgp_community_alias_cmd,
- "[no$no] bgp community alias WORD$community WORD$alias",
+ "[no$no] bgp community alias WORD$community ALIAS_NAME$alias_name",
NO_STR BGP_STR
"Add community specific parameters\n"
"Create an alias for a community\n"
@@ -1584,7 +1585,7 @@ DEFPY(bgp_community_alias, bgp_community_alias_cmd,
memset(&ca1, 0, sizeof(ca1));
memset(&ca2, 0, sizeof(ca2));
strlcpy(ca1.community, community, sizeof(ca1.community));
- strlcpy(ca1.alias, alias, sizeof(ca1.alias));
+ strlcpy(ca1.alias, alias_name, sizeof(ca1.alias));
lookup_community = bgp_ca_community_lookup(&ca1);
lookup_alias = bgp_ca_alias_lookup(&ca1);
@@ -8116,6 +8117,23 @@ static int peer_advertise_map_set_vty(struct vty *vty, const char *ip_str,
return bgp_vty_return(vty, ret);
}
+DEFPY (bgp_condadv_period,
+ bgp_condadv_period_cmd,
+ "[no$no] bgp conditional-advertisement timer (5-240)$period",
+ NO_STR
+ BGP_STR
+ "Conditional advertisement settings\n"
+ "Set period to rescan BGP table to check if condition is met\n"
+ "Period between BGP table scans, in seconds; default 60\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ bgp->condition_check_period =
+ no ? DEFAULT_CONDITIONAL_ROUTES_POLL_TIME : period;
+
+ return CMD_SUCCESS;
+}
+
DEFPY (neighbor_advertise_map,
neighbor_advertise_map_cmd,
"[no$no] neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor advertise-map WORD$advertise_str <exist-map|non-exist-map>$exist WORD$condition_str",
@@ -18146,6 +18164,13 @@ int bgp_config_write(struct vty *vty)
vty_out(vty, " timers bgp %u %u\n",
bgp->default_keepalive, bgp->default_holdtime);
+ /* Conditional advertisement timer configuration */
+ if (bgp->condition_check_period
+ != DEFAULT_CONDITIONAL_ROUTES_POLL_TIME)
+ vty_out(vty,
+ " bgp conditional-advertisement timer %u\n",
+ bgp->condition_check_period);
+
/* peer-group */
for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) {
bgp_config_write_peer_global(vty, bgp, group->conf);
@@ -19434,6 +19459,7 @@ void bgp_vty_init(void)
install_element(BGP_VPNV6_NODE, &no_neighbor_unsuppress_map_cmd);
/* "neighbor advertise-map" commands. */
+ install_element(BGP_NODE, &bgp_condadv_period_cmd);
install_element(BGP_NODE, &neighbor_advertise_map_hidden_cmd);
install_element(BGP_IPV4_NODE, &neighbor_advertise_map_cmd);
install_element(BGP_IPV4M_NODE, &neighbor_advertise_map_cmd);
@@ -20944,4 +20970,6 @@ void community_alias_vty(void)
/* Community-list. */
install_element(CONFIG_NODE, &bgp_community_alias_cmd);
+
+ bgp_community_alias_command_completion_setup();
}
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index 002758bda9..24652ee93a 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -750,6 +750,9 @@ bool bgp_zebra_nexthop_set(union sockunion *local, union sockunion *remote,
? peer->conf_if
: peer->ifname,
peer->bgp->vrf_id);
+ else if (peer->update_if)
+ ifp = if_lookup_by_name(peer->update_if,
+ peer->bgp->vrf_id);
} else if (peer->update_if)
ifp = if_lookup_by_name(peer->update_if,
peer->bgp->vrf_id);
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index acade16ef2..6c9ec0ebaa 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -656,14 +656,9 @@ int bgp_confederation_peers_add(struct bgp *bgp, as_t as)
if (bgp_confederation_peers_check(bgp, as))
return -1;
- if (bgp->confed_peers)
- bgp->confed_peers =
- XREALLOC(MTYPE_BGP_CONFED_LIST, bgp->confed_peers,
- (bgp->confed_peers_cnt + 1) * sizeof(as_t));
- else
- bgp->confed_peers =
- XMALLOC(MTYPE_BGP_CONFED_LIST,
- (bgp->confed_peers_cnt + 1) * sizeof(as_t));
+ bgp->confed_peers =
+ XREALLOC(MTYPE_BGP_CONFED_LIST, bgp->confed_peers,
+ (bgp->confed_peers_cnt + 1) * sizeof(as_t));
bgp->confed_peers[bgp->confed_peers_cnt] = as;
bgp->confed_peers_cnt++;
@@ -2317,13 +2312,6 @@ int peer_deactivate(struct peer *peer, afi_t afi, safi_t safi)
peer->afc[afi][safi] = 0;
group = peer->group;
- if (peer_af_delete(peer, afi, safi) != 0) {
- flog_err(
- EC_BGP_PEER_DELETE,
- "couldn't delete af structure for peer %s(%s, %s)",
- peer->host, afi2str(afi), safi2str(safi));
- }
-
for (ALL_LIST_ELEMENTS(group->peer, node, nnode, tmp_peer)) {
ret |= non_peergroup_deactivate_af(tmp_peer, afi, safi);
}
@@ -3180,6 +3168,7 @@ static struct bgp *bgp_create(as_t *as, const char *name,
bgp->lb_ref_bw = BGP_LINK_BW_REF_BW;
bgp->lb_handling = BGP_LINK_BW_ECMP;
bgp->reject_as_sets = false;
+ bgp->condition_check_period = DEFAULT_CONDITIONAL_ROUTES_POLL_TIME;
bgp_addpath_init_bgp_data(&bgp->tx_addpath);
bgp->as = *as;
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index d2e8cce997..e6a828a2c0 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -748,6 +748,7 @@ struct bgp {
struct work_queue *process_queue;
/* BGP Conditional advertisement */
+ uint32_t condition_check_period;
uint32_t condition_filter_count;
struct thread *t_condition_check;
diff --git a/bgpd/subdir.am b/bgpd/subdir.am
index 53225192f2..cb9af08c95 100644
--- a/bgpd/subdir.am
+++ b/bgpd/subdir.am
@@ -219,17 +219,17 @@ bgpd_bgp_btoa_LDADD = bgpd/libbgp.a $(RFPLDADD) lib/libfrr.la $(LIBYANG_LIBS) $(
bgpd_bgpd_snmp_la_SOURCES = bgpd/bgp_snmp.c bgpd/bgp_mplsvpn_snmp.c
bgpd_bgpd_snmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
-bgpd_bgpd_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+bgpd_bgpd_snmp_la_LDFLAGS = $(MODULE_LDFLAGS)
bgpd_bgpd_snmp_la_LIBADD = lib/libfrrsnmp.la
bgpd_bgpd_rpki_la_SOURCES = bgpd/bgp_rpki.c
bgpd_bgpd_rpki_la_CFLAGS = $(AM_CFLAGS) $(RTRLIB_CFLAGS)
-bgpd_bgpd_rpki_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+bgpd_bgpd_rpki_la_LDFLAGS = $(MODULE_LDFLAGS)
bgpd_bgpd_rpki_la_LIBADD = $(RTRLIB_LIBS)
bgpd_bgpd_bmp_la_SOURCES = bgpd/bgp_bmp.c
bgpd_bgpd_bmp_la_LIBADD = lib/libfrrcares.la
-bgpd_bgpd_bmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+bgpd_bgpd_bmp_la_LDFLAGS = $(MODULE_LDFLAGS)
clippy_scan += \
bgpd/bgp_bmp.c \
diff --git a/configure.ac b/configure.ac
index a23011b755..c86f47d073 100644
--- a/configure.ac
+++ b/configure.ac
@@ -264,11 +264,11 @@ AC_C_FLAG([-std=gnu11], [CC="$ac_cc"], [CC="$CC -std=gnu11"])
dnl if the user has specified any CFLAGS, override our settings
if test "$enable_gcov" = "yes"; then
if test "$orig_cflags" = ""; then
- AC_C_FLAG([-coverage])
+ AC_C_FLAG([--coverage])
AC_C_FLAG([-O0])
fi
- AC_LDFLAGS="${AC_LDFLAGS} -lgcov"
+ AC_LDFLAGS="${AC_LDFLAGS} --coverage"
fi
if test "$enable_clang_coverage" = "yes"; then
@@ -492,7 +492,7 @@ _LT_CONFIG_LIBTOOL([
sed -e 's%func_warning ".*has not been installed in%true #\0%' -i libtool || true
])
if test "$enable_static_bin" = "yes"; then
- AC_LDFLAGS="-static"
+ AC_LDFLAGS_EXEC="-static"
if test "$enable_static" != "yes"; then
AC_MSG_ERROR([The --enable-static-bin option must be combined with --enable-static.])
fi
@@ -501,6 +501,7 @@ if test "$enable_shared" != "yes"; then
AC_MSG_ERROR([FRR cannot be built with --disable-shared. If you want statically linked daemons, use --enable-shared --enable-static --enable-static-bin])
fi
AC_SUBST([AC_LDFLAGS])
+AC_SUBST([AC_LDFLAGS_EXEC])
AM_CONDITIONAL([STATIC_BIN], [test "$enable_static_bin" = "yes"])
AC_ARG_ENABLE([rpath],
@@ -703,7 +704,7 @@ AC_ARG_ENABLE([thread-sanitizer],
AC_ARG_ENABLE([memory-sanitizer],
AS_HELP_STRING([--enable-memory-sanitizer], [enable MemorySanitizer support for detecting uninitialized memory reads]))
AC_ARG_ENABLE([undefined-sanitizer],
- AS_HELP_STRING([--undefined-sanitizer], [enable UndefinedBehaviorSanitizer support for detecting undefined behavior]))
+ AS_HELP_STRING([--enable-undefined-sanitizer], [enable UndefinedBehaviorSanitizer support for detecting undefined behavior]))
AC_ARG_WITH([crypto],
AS_HELP_STRING([--with-crypto=<internal|openssl>], [choose between different implementations of cryptographic functions(default value is --with-crypto=internal)]))
AC_ARG_WITH([frr-format],
@@ -2514,6 +2515,7 @@ AC_SUBST([frr_statedir])
AC_DEFINE_UNQUOTED([LDPD_SOCKET], ["$frr_statedir%s%s/ldpd.sock"], [ldpd control socket])
AC_DEFINE_UNQUOTED([ZEBRA_SERV_PATH], ["$frr_statedir%s%s/zserv.api"], [zebra api socket])
AC_DEFINE_UNQUOTED([BFDD_CONTROL_SOCKET], ["$frr_statedir%s%s/bfdd.sock"], [bfdd control socket])
+AC_DEFINE_UNQUOTED([OSPFD_GR_STATE], ["$frr_statedir%s/ospfd-gr.json"], [ospfd GR state information])
AC_DEFINE_UNQUOTED([DAEMON_VTY_DIR], ["$frr_statedir%s%s"], [daemon vty directory])
AC_DEFINE_UNQUOTED([DAEMON_DB_DIR], ["$frr_statedir"], [daemon database directory])
diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst
index 58b9c36e01..dae175a732 100644
--- a/doc/developer/workflow.rst
+++ b/doc/developer/workflow.rst
@@ -880,6 +880,104 @@ doesn't warrant an update to checkpatch, it is documented here.
| should be wrapped in parentheses | all usages of the macro, which would be highly disruptive. |
+------------------------------------------+---------------------------------------------------------------+
+Types of configurables
+----------------------
+
+.. note::
+
+ This entire section essentially just argues to not make configuration
+ unnecessarily involved for the user. Rather than rules, this is more of
+ a list of conclusions intended to help make FRR usable for operators.
+
+
+Almost every feature FRR has comes with its own set of switches and options.
+There are several stages at which configuration can be applied. In order of
+preference, these are:
+
+- at configuration/runtime, through YANG.
+
+ This is the preferred way for all FRR knobs. Not all daemons and features
+ are fully YANGified yet, so in some cases new features cannot rely on a
+ YANG interface. If a daemon already implements a YANG interface (even
+ partial), new CLI options must be implemented through a YANG model.
+
+ .. warning::
+
+ Unlike everything else in this section being guidelines with some slack,
+ implementing and using a YANG interface for new CLI options in (even
+ partially!) YANGified daemons is a hard requirement.
+
+
+- at configuration/runtime, through the CLI.
+
+ The "good old" way for all regular configuration. More involved for users
+ to automate *correctly* than YANG.
+
+- at startup, by loading additional modules.
+
+ If a feature introduces a dependency on additional libraries (e.g. libsnmp,
+ rtrlib, etc.), this is the best way to encapsulate the dependency. Having
+ a separate module allows the distribution to create a separate package
+ with the extra dependency, so FRR can still be installed without pulling
+ everything in.
+
+ A module may also be appropriate if a feature is large and reasonably well
+ isolated. Reducing the amount of running the code is a security benefit,
+ so even if there are no new external dependencies, modules can be useful.
+
+ While modules cannot currently be loaded at runtime, this is a tradeoff
+ decision that was made to allow modules to change/extend code that is very
+ hard to (re)adjust at runtime. If there is a case for runtime (un)loading
+ of modules, this tradeoff can absolutely be reevaluated.
+
+- at startup, with command line options.
+
+ This interface is only appropriate for options that have an effect very
+ early in FRR startup, i.e. before configuration is loaded. Anything that
+ affects configuration load itself should be here, as well as options
+ changing the environment FRR runs in.
+
+ If a tunable can be changed at runtime, a command line option is only
+ acceptable if the configured value has an effect before configuration is
+ loaded (e.g. zebra reads routes from the kernel before loading config, so
+ the netlink buffer size is an appropriate command line option.)
+
+- at compile time, with ``./configure`` options.
+
+ This is the absolute last preference for tunables, since the distribution
+ needs to make the decision for the user and/or the user needs to rebuild
+ FRR in order to change the option.
+
+ "Good" configure options do one of three things:
+
+ - set distribution-specific parameters, most prominently all the path
+ options. File system layout is a distribution/packaging choice, so the
+ user would hopefully never need to adjust these.
+
+ - changing toolchain behavior, e.g. instrumentation, warnings,
+ optimizations and sanitizers.
+
+ - enabling/disabling parts of the build, especially if they need
+ additional dependencies. Being able to build only parts of FRR, or
+ without some library, is useful. **The only effect these options should
+ have is adding or removing files from the build result.** If a knob
+ in this category causes the same binary to exist in different variants,
+ it is likely implemented incorrectly!
+
+ .. note::
+
+ This last guideline is currently ignored by several configure options.
+ ``vtysh`` in general depends on the entire list of enabled daemons,
+ and options like ``--enable-bgp-vnc`` and ``--enable-ospfapi`` change
+ daemons internally. Consider this more of an "ideal" than a "rule".
+
+
+Whenever adding new knobs, please try reasonably hard to go up as far as
+possible on the above list. Especially ``./configure`` flags are often enough
+the "easy way out" but should be avoided when at all possible. To a lesser
+degree, the same applies to command line options.
+
+
Compile-time conditional code
-----------------------------
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index b5950538dd..88f0483901 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -401,6 +401,22 @@ Route Selection
paths learned from any of eBGP, iBGP, or confederation neighbors will
be multipath if they are otherwise considered equal cost.
+.. clicmd:: maximum-paths (1-128)
+
+ Sets the maximum-paths value used for ecmp calculations for this
+ bgp instance in EBGP. The maximum value listed, 128, can be limited by
+ the ecmp cli for bgp or if the daemon was compiled with a lower
+ ecmp value. This value can also be set in ipv4/ipv6 unicast/labeled
+ unicast to only affect those particular afi/safi's.
+
+.. clicmd:: maximum-paths ibgp (1-128) [equal-cluster-length]
+
+ Sets the maximum-paths value used for ecmp calculations for this
+ bgp instance in IBGP. The maximum value listed, 128, can be limited by
+ the ecmp cli for bgp or if the daemon was compiled with a lower
+ ecmp value. This value can also be set in ipv4/ipv6 unicast/labeled
+ unicast to only affect those particular afi/safi's.
+
.. _bgp-distance:
Administrative Distance Metrics
@@ -1442,12 +1458,13 @@ Configuring Peers
session will be established via IPv6 link locals. Use ``internal`` for iBGP
and ``external`` for eBGP sessions, or specify an ASN if you wish.
-.. clicmd:: neighbor PEER next-hop-self [all]
+.. clicmd:: neighbor PEER next-hop-self [force]
This command specifies an announced route's nexthop as being equivalent to
- the address of the bgp router if it is learned via eBGP. If the optional
- keyword `all` is specified the modification is done also for routes learned
- via iBGP.
+ the address of the bgp router if it is learned via eBGP. This will also
+ bypass third-party next-hops in favor of the local bgp address. If the
+ optional keyword ``force`` is specified the modification is done also for
+ routes learned via iBGP.
.. clicmd:: neighbor PEER attribute-unchanged [{as-path|next-hop|med}]
@@ -2177,6 +2194,12 @@ communities attribute.
The following commands can be used in route maps:
+.. clicmd:: match alias WORD
+
+ This command performs match to BGP updates using community alias WORD. When
+ the one of BGP communities value match to the one of community alias value in
+ community alias, it is match.
+
.. clicmd:: match community WORD exact-match [exact-match]
This command perform match to BGP updates using community list WORD. When
@@ -3016,10 +3039,13 @@ The conditional BGP announcements are sent in addition to the normal
announcements that a BGP router sends to its peer.
The conditional advertisement process is triggered by the BGP scanner process,
-which runs every 60 seconds. This means that the maximum time for the conditional
-advertisement to take effect is 60 seconds. The conditional advertisement can take
-effect depending on when the tracked route is removed from the BGP table and
-when the next instance of the BGP scanner occurs.
+which runs every 60 by default. This means that the maximum time for the
+conditional advertisement to take effect is the value of the process timer.
+
+As an optimization, while the process always runs on each timer expiry, it
+determines whether or not the conditional advertisement policy or the routing
+table has changed; if neither have changed, no processing is necessary and the
+scanner exits early.
.. clicmd:: neighbor A.B.C.D advertise-map NAME [exist-map|non-exist-map] NAME
@@ -3027,6 +3053,11 @@ when the next instance of the BGP scanner occurs.
exist-map or non-exist-map command in BGP table and conditionally advertises
the routes specified by advertise-map command.
+.. clicmd:: bgp conditional-advertisement timer (5-240)
+
+ Set the period to rerun the conditional advertisement scanner process. The
+ default is 60 seconds.
+
Sample Configuration
^^^^^^^^^^^^^^^^^^^^^
.. code-block:: frr
diff --git a/doc/user/filter.rst b/doc/user/filter.rst
index 1fb9beccdc..cbbcd47dc3 100644
--- a/doc/user/filter.rst
+++ b/doc/user/filter.rst
@@ -137,6 +137,15 @@ Showing ip prefix-list
.. clicmd:: show ip prefix-list detail
.. clicmd:: show ip prefix-list detail NAME
+.. clicmd:: debug prefix-list NAME match <A.B.C.D/M|X:X::X:X/M> [address-mode]
+
+ Execute the prefix list matching code for the specified list and prefix.
+ Shows which entry matched, if any. (``address-mode`` is used for
+ PIM RP lookups and skips prefix length checks.)
+
+ The return value from this command is success only if the prefix-list
+ result is to permit the prefix, so the command can be used in scripting.
+
Clear counter of ip prefix-list
-------------------------------
diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst
index 04ee1643d8..5344f4cb05 100644
--- a/doc/user/ospf6d.rst
+++ b/doc/user/ospf6d.rst
@@ -85,6 +85,61 @@ OSPF6 router
change to take effect, user can use this cli instead of restarting the
ospf6d daemon.
+.. _ospf6-debugging:
+
+OSPFv3 Debugging
+================
+
+The following debug commands are supported:
+
+.. clicmd:: debug ospf6 abr
+
+ Toggle OSPFv3 ABR debugging messages.
+
+.. clicmd:: debug ospf6 asbr
+
+ Toggle OSPFv3 ASBR debugging messages.
+
+.. clicmd:: debug ospf6 border-routers
+
+ Toggle OSPFv3 border router debugging messages.
+
+.. clicmd:: debug ospf6 flooding
+
+ Toggle OSPFv3 flooding debugging messages.
+
+.. clicmd:: debug ospf6 interface
+
+ Toggle OSPFv3 interface related debugging messages.
+
+.. clicmd:: debug ospf6 lsa
+
+ Toggle OSPFv3 Link State Advertisements debugging messages.
+
+.. clicmd:: debug ospf6 message
+
+ Toggle OSPFv3 message exchange debugging messages.
+
+.. clicmd:: debug ospf6 neighbor
+
+ Toggle OSPFv3 neighbor interaction debugging messages.
+
+.. clicmd:: debug ospf6 nssa
+
+ Toggle OSPFv3 Not So Stubby Area (NSSA) debugging messages.
+
+.. clicmd:: debug ospf6 route
+
+ Toggle OSPFv3 routes debugging messages.
+
+.. clicmd:: debug ospf6 spf
+
+ Toggle OSPFv3 Shortest Path calculation debugging messages.
+
+.. clicmd:: debug ospf6 zebra
+
+ Toggle OSPFv3 zebra interaction debugging messages.
+
.. _ospf6-area:
OSPF6 area
@@ -332,13 +387,3 @@ Larger example with policy and various options set:
ipv6 access-class access6
exec-timeout 0 0
!
-
-
-Configuration Limits
-====================
-
-Ospf6d currently supports 100 interfaces addresses if MTU is set to
-default value, and 200 interface addresses if MTU is set to jumbo
-packet size or larger.
-
-
diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst
index 8d67ec865f..e8ca394727 100644
--- a/doc/user/ospfd.rst
+++ b/doc/user/ospfd.rst
@@ -710,8 +710,17 @@ Redistribution
-Graceful Restart Helper
-=======================
+Graceful Restart
+================
+
+.. clicmd:: graceful-restart [grace-period (1-1800)]
+
+
+ Configure Graceful Restart (RFC 3623) restarting support.
+ When enabled, the default grace period is 120 seconds.
+
+ To perform a graceful shutdown, the "graceful-restart prepare ip ospf"
+ EXEC-level command needs to be issued before restarting the ospfd daemon.
.. clicmd:: graceful-restart helper-only [A.B.C.D]
@@ -743,6 +752,17 @@ Graceful Restart Helper
restarts. By default, it supports both planned and
unplanned outages.
+
+.. clicmd:: graceful-restart prepare ip ospf
+
+
+ Initiate a graceful restart for all OSPF instances configured with the
+ "graceful-restart" command. The ospfd daemon should be restarted during
+ the instance-specific grace period, otherwise the graceful restart will fail.
+
+ This is an EXEC-level command.
+
+
.. _showing-ospf-information:
Showing Information
diff --git a/doc/user/pim.rst b/doc/user/pim.rst
index 83d19d6188..6f9aa289b4 100644
--- a/doc/user/pim.rst
+++ b/doc/user/pim.rst
@@ -93,11 +93,13 @@ Certain signals have special meanings to *pimd*.
down. This command is vrf aware, to configure for a vrf, enter the vrf
submode.
-.. clicmd:: ip pim join-prune-interval (60-600)
+.. clicmd:: ip pim join-prune-interval (5-600)
Modify the join/prune interval that pim uses to the new value. Time is
specified in seconds. This command is vrf aware, to configure for a vrf,
- enter the vrf submode.
+ enter the vrf submode. The default time is 60 seconds. If you enter
+ a value smaller than 60 seconds be aware that this can and will affect
+ convergence at scale.
.. clicmd:: ip pim keep-alive-timer (31-60000)
@@ -199,7 +201,7 @@ is in a vrf, enter the interface command with the vrf keyword at the end.
Set the DR Priority for the interface. This command is useful to allow the
user to influence what node becomes the DR for a lan segment.
-.. clicmd:: ip pim hello (1-180) (1-180)
+.. clicmd:: ip pim hello (1-180) (1-630)
Set the pim hello and hold interval for a interface.
diff --git a/fpm/subdir.am b/fpm/subdir.am
index a645ca2b03..b5988137e0 100644
--- a/fpm/subdir.am
+++ b/fpm/subdir.am
@@ -4,7 +4,7 @@ lib_LTLIBRARIES += fpm/libfrrfpm_pb.la
endif
endif
-fpm_libfrrfpm_pb_la_LDFLAGS = -version-info 0:0:0
+fpm_libfrrfpm_pb_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
fpm_libfrrfpm_pb_la_CPPFLAGS = $(AM_CPPFLAGS) $(PROTOBUF_C_CFLAGS)
fpm_libfrrfpm_pb_la_SOURCES = \
fpm/fpm.h \
diff --git a/grpc/subdir.am b/grpc/subdir.am
index d9ec365ba8..cbebd72323 100644
--- a/grpc/subdir.am
+++ b/grpc/subdir.am
@@ -2,7 +2,7 @@ if GRPC
lib_LTLIBRARIES += grpc/libfrrgrpc_pb.la
endif
-grpc_libfrrgrpc_pb_la_LDFLAGS = -version-info 0:0:0
+grpc_libfrrgrpc_pb_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
grpc_libfrrgrpc_pb_la_CPPFLAGS = $(AM_CPPFLAGS) $(GRPC_CXXFLAGS)
if GRPC
diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c
index 814ba8fc2a..5c013d634b 100644
--- a/isisd/isis_lsp.c
+++ b/isisd/isis_lsp.c
@@ -401,93 +401,77 @@ static void lsp_seqno_update(struct isis_lsp *lsp0)
return;
}
-static bool isis_level2_adj_up(struct isis_area *curr_area)
+bool isis_level2_adj_up(struct isis_area *area)
{
struct listnode *node, *cnode;
struct isis_circuit *circuit;
struct list *adjdb;
struct isis_adjacency *adj;
- struct isis *isis = curr_area->isis;
- struct isis_area *area;
- /* lookup for a Level2 adjacency up in another area */
- for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
- if (area->area_tag
- && strcmp(area->area_tag, curr_area->area_tag) == 0)
- continue;
+ if (area->is_type == IS_LEVEL_1)
+ return false;
- for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit)) {
- if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
- adjdb = circuit->u.bc.adjdb[1];
- if (!adjdb || !adjdb->count)
- continue;
+ for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit)) {
+ if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
+ adjdb = circuit->u.bc.adjdb[1];
+ if (!adjdb || !adjdb->count)
+ continue;
- for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj)) {
- if (adj->level != ISIS_ADJ_LEVEL1
- && adj->adj_state == ISIS_ADJ_UP)
- return true;
- }
- } else if (circuit->circ_type == CIRCUIT_T_P2P
- && circuit->u.p2p.neighbor) {
- adj = circuit->u.p2p.neighbor;
+ for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj)) {
if (adj->level != ISIS_ADJ_LEVEL1
&& adj->adj_state == ISIS_ADJ_UP)
return true;
}
+ } else if (circuit->circ_type == CIRCUIT_T_P2P
+ && circuit->u.p2p.neighbor) {
+ adj = circuit->u.p2p.neighbor;
+ if (adj->level != ISIS_ADJ_LEVEL1
+ && adj->adj_state == ISIS_ADJ_UP)
+ return true;
}
}
+
return false;
}
-static void isis_reset_attach_bit(struct isis_adjacency *curr_adj)
+static void isis_reset_attach_bit(struct isis_adjacency *adj)
{
- struct listnode *node;
- struct isis_area *curr_area = curr_adj->circuit->area;
- struct isis *isis = curr_area->isis;
- struct isis_area *area;
+ struct isis_area *area = adj->circuit->area;
struct lspdb_head *head;
struct isis_lsp *lsp;
uint8_t lspid[ISIS_SYS_ID_LEN + 2];
- /* If new adjaceny is up and area is level2 or level1and2 verify if
- * we have LSPs in other areas that should now set the attach bit.
- *
- * If adjacenty is down, verify if we no longer have another level2
- * or level1and2 areas so that we should now remove the attach bit.
+ /*
+ * If an L2 adjacency changed its state in L-1-2 area, we have to:
+ * - set the attached bit in L1 LSPs if it's the first L2 adjacency
+ * - remove the attached bit in L1 LSPs if it's the last L2 adjacency
*/
- if (curr_area->is_type == IS_LEVEL_1)
- return;
- for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
- if (area->area_tag
- && strcmp(area->area_tag, curr_area->area_tag) == 0)
- continue;
+ if (area->is_type != IS_LEVEL_1_AND_2 || adj->level == ISIS_ADJ_LEVEL1)
+ return;
- if (!area->attached_bit_send)
- continue;
+ if (!area->attached_bit_send)
+ return;
- head = &area->lspdb[IS_LEVEL_1 - 1];
- memset(lspid, 0, ISIS_SYS_ID_LEN + 2);
- memcpy(lspid, area->isis->sysid, ISIS_SYS_ID_LEN);
+ head = &area->lspdb[IS_LEVEL_1 - 1];
+ memset(lspid, 0, ISIS_SYS_ID_LEN + 2);
+ memcpy(lspid, area->isis->sysid, ISIS_SYS_ID_LEN);
- lsp = lsp_search(head, lspid);
- if (!lsp)
- continue;
+ lsp = lsp_search(head, lspid);
+ if (!lsp)
+ return;
- if (curr_adj->adj_state == ISIS_ADJ_UP
- && !(lsp->hdr.lsp_bits & LSPBIT_ATT)) {
- sched_debug(
- "ISIS (%s): adj going up regenerate lsp-bits",
- area->area_tag);
- lsp_regenerate_schedule(area, IS_LEVEL_1, 0);
- } else if (curr_adj->adj_state == ISIS_ADJ_DOWN
- && lsp->hdr.lsp_bits & LSPBIT_ATT
- && !isis_level2_adj_up(area)) {
- sched_debug(
- "ISIS (%s): adj going down regenerate lsp-bits",
- area->area_tag);
- lsp_regenerate_schedule(area, IS_LEVEL_1, 0);
- }
+ if (adj->adj_state == ISIS_ADJ_UP
+ && !(lsp->hdr.lsp_bits & LSPBIT_ATT)) {
+ sched_debug("ISIS (%s): adj going up regenerate lsp-bits",
+ area->area_tag);
+ lsp_regenerate_schedule(area, IS_LEVEL_1, 0);
+ } else if (adj->adj_state == ISIS_ADJ_DOWN
+ && (lsp->hdr.lsp_bits & LSPBIT_ATT)
+ && !isis_level2_adj_up(area)) {
+ sched_debug("ISIS (%s): adj going down regenerate lsp-bits",
+ area->area_tag);
+ lsp_regenerate_schedule(area, IS_LEVEL_1, 0);
}
}
@@ -495,7 +479,7 @@ static uint8_t lsp_bits_generate(int level, int overload_bit, int attached_bit,
struct isis_area *area)
{
uint8_t lsp_bits = 0;
- if (level == IS_LEVEL_1)
+ if (area->is_type == IS_LEVEL_1)
lsp_bits = IS_LEVEL_1;
else
lsp_bits = IS_LEVEL_1_AND_2;
diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h
index f3d9f61bcf..cac5f0d733 100644
--- a/isisd/isis_lsp.h
+++ b/isisd/isis_lsp.h
@@ -77,6 +77,8 @@ int _lsp_regenerate_schedule(struct isis_area *area, int level,
int lsp_generate_pseudo(struct isis_circuit *circuit, int level);
int lsp_regenerate_schedule_pseudo(struct isis_circuit *circuit, int level);
+bool isis_level2_adj_up(struct isis_area *area);
+
struct isis_lsp *lsp_new(struct isis_area *area, uint8_t *lsp_id,
uint16_t rem_lifetime, uint32_t seq_num,
uint8_t lsp_bits, uint16_t checksum,
diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c
index 3e8ec8817e..45e89897ff 100644
--- a/isisd/isis_spf.c
+++ b/isisd/isis_spf.c
@@ -1070,8 +1070,8 @@ end:
*/
if ((lsp->hdr.lsp_bits & LSPBIT_ATT) == LSPBIT_ATT
&& !spftree->area->attached_bit_rcv_ignore
- && spftree->area->is_type == IS_LEVEL_1
- && !isis_area_count(spftree->area->isis, IS_LEVEL_2)) {
+ && (spftree->area->is_type & IS_LEVEL_1)
+ && !isis_level2_adj_up(spftree->area)) {
struct prefix_pair ip_info = { {0} };
if (IS_DEBUG_RTE_EVENTS)
zlog_debug("ISIS-Spf (%s): add default %s route",
diff --git a/isisd/isisd.c b/isisd/isisd.c
index 6c1308af0a..43efa0164d 100644
--- a/isisd/isisd.c
+++ b/isisd/isisd.c
@@ -469,22 +469,6 @@ int isis_area_get(struct vty *vty, const char *area_tag)
return CMD_SUCCESS;
}
-/* return the number of Level1 and level-1-2 routers or
- * the number of Level2 and level-1-2 routers configured
- */
-int isis_area_count(const struct isis *isis, int levels)
-{
- struct isis_area *area;
- struct listnode *node;
- int count = 0;
-
- for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area))
- if (area->is_type & levels)
- count++;
-
- return count;
-}
-
void isis_area_destroy(struct isis_area *area)
{
struct listnode *node, *nnode;
diff --git a/isisd/isisd.h b/isisd/isisd.h
index 60dcf066dd..64fbf78a07 100644
--- a/isisd/isisd.h
+++ b/isisd/isisd.h
@@ -269,7 +269,6 @@ struct isis_area *isis_area_lookup(const char *, vrf_id_t vrf_id);
struct isis_area *isis_area_lookup_by_vrf(const char *area_tag,
const char *vrf_name);
int isis_area_get(struct vty *vty, const char *area_tag);
-int isis_area_count(const struct isis *isis, int levels);
void isis_area_destroy(struct isis_area *area);
void isis_filter_update(struct access_list *access);
void isis_prefix_list_update(struct prefix_list *plist);
diff --git a/isisd/subdir.am b/isisd/subdir.am
index 4243bd60cf..3e5816c16b 100644
--- a/isisd/subdir.am
+++ b/isisd/subdir.am
@@ -139,7 +139,7 @@ nodist_isisd_isisd_SOURCES = \
isisd_isisd_snmp_la_SOURCES = isisd/isis_snmp.c
isisd_isisd_snmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
-isisd_isisd_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+isisd_isisd_snmp_la_LDFLAGS = $(MODULE_LDFLAGS)
isisd_isisd_snmp_la_LIBADD = lib/libfrrsnmp.la
# Building fabricd
diff --git a/ldpd/subdir.am b/ldpd/subdir.am
index b7e2ab72d6..083effb703 100644
--- a/ldpd/subdir.am
+++ b/ldpd/subdir.am
@@ -65,5 +65,5 @@ ldpd_ldpd_LDADD = ldpd/libldp.a lib/libfrr.la $(LIBCAP)
ldpd_ldpd_snmp_la_SOURCES = ldpd/ldp_snmp.c
ldpd_ldpd_snmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
-ldpd_ldpd_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+ldpd_ldpd_snmp_la_LDFLAGS = $(MODULE_LDFLAGS)
ldpd_ldpd_snmp_la_LIBADD = lib/libfrrsnmp.la
diff --git a/lib/command.c b/lib/command.c
index 9dac60599c..fe17c68a8b 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -160,6 +160,9 @@ static bool vty_check_node_for_xpath_decrement(enum node_type target_node,
|| node == BGP_FLOWSPECV6_NODE))
return false;
+ if (target_node == INTERFACE_NODE && node == LINK_PARAMS_NODE)
+ return false;
+
return true;
}
diff --git a/lib/command.h b/lib/command.h
index 13bd61e9fd..2b50bc2374 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -438,6 +438,8 @@ struct cmd_node {
#define BFD_PROFILE_STR "BFD profile.\n"
#define BFD_PROFILE_NAME_STR "BFD profile name.\n"
#define SHARP_STR "Sharp Routing Protocol\n"
+#define OSPF_GR_STR \
+ "OSPF non-stop forwarding (NSF) also known as OSPF Graceful Restart\n"
#define CMD_VNI_RANGE "(1-16777215)"
#define CONF_BACKUP_EXT ".sav"
diff --git a/lib/plist.c b/lib/plist.c
index 0ee02f8a0b..2b42c43764 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -750,7 +750,7 @@ static const char *prefix_list_type_str(struct prefix_list_entry *pentry)
}
static int prefix_list_entry_match(struct prefix_list_entry *pentry,
- const struct prefix *p)
+ const struct prefix *p, bool address_mode)
{
int ret;
@@ -761,6 +761,9 @@ static int prefix_list_entry_match(struct prefix_list_entry *pentry,
if (!ret)
return 0;
+ if (address_mode)
+ return 1;
+
/* In case of le nor ge is specified, exact match is performed. */
if (!pentry->le && !pentry->ge) {
if (pentry->prefix.prefixlen != p->prefixlen)
@@ -777,14 +780,15 @@ static int prefix_list_entry_match(struct prefix_list_entry *pentry,
return 1;
}
-enum prefix_list_type prefix_list_apply_which_prefix(
+enum prefix_list_type prefix_list_apply_ext(
struct prefix_list *plist,
- const struct prefix **which,
- const void *object)
+ const struct prefix_list_entry **which,
+ union prefixconstptr object,
+ bool address_mode)
{
struct prefix_list_entry *pentry, *pbest = NULL;
- const struct prefix *p = (const struct prefix *)object;
+ const struct prefix *p = object.p;
const uint8_t *byte = p->u.val;
size_t depth;
size_t validbits = p->prefixlen;
@@ -809,7 +813,7 @@ enum prefix_list_type prefix_list_apply_which_prefix(
pentry = pentry->next_best) {
if (pbest && pbest->seq < pentry->seq)
continue;
- if (prefix_list_entry_match(pentry, p))
+ if (prefix_list_entry_match(pentry, p, address_mode))
pbest = pentry;
}
@@ -830,7 +834,7 @@ enum prefix_list_type prefix_list_apply_which_prefix(
pentry = pentry->next_best) {
if (pbest && pbest->seq < pentry->seq)
continue;
- if (prefix_list_entry_match(pentry, p))
+ if (prefix_list_entry_match(pentry, p, address_mode))
pbest = pentry;
}
break;
@@ -838,7 +842,7 @@ enum prefix_list_type prefix_list_apply_which_prefix(
if (which) {
if (pbest)
- *which = &pbest->prefix;
+ *which = pbest;
else
*which = NULL;
}
@@ -1296,6 +1300,51 @@ DEFPY (clear_ipv6_prefix_list,
return vty_clear_prefix_list(vty, AFI_IP6, prefix_list, prefix_str);
}
+DEFPY (debug_prefix_list_match,
+ debug_prefix_list_match_cmd,
+ "debug prefix-list WORD$prefix-list match <A.B.C.D/M|X:X::X:X/M>"
+ " [address-mode$addr_mode]",
+ DEBUG_STR
+ "Prefix-list test access\n"
+ "Name of a prefix list\n"
+ "Test prefix for prefix list result\n"
+ "Prefix to test in ip prefix-list\n"
+ "Prefix to test in ipv6 prefix-list\n"
+ "Use address matching mode (PIM RP)\n")
+{
+ struct prefix_list *plist;
+ const struct prefix_list_entry *entry = NULL;
+ enum prefix_list_type ret;
+
+ plist = prefix_list_lookup(family2afi(match->family), prefix_list);
+ if (!plist) {
+ vty_out(vty, "%% no prefix list named %s for AFI %s\n",
+ prefix_list, afi2str(family2afi(match->family)));
+ return CMD_WARNING;
+ }
+
+ ret = prefix_list_apply_ext(plist, &entry, match, !!addr_mode);
+
+ vty_out(vty, "%s prefix list %s yields %s for %pFX, ",
+ afi2str(family2afi(match->family)), prefix_list,
+ ret == PREFIX_DENY ? "DENY" : "PERMIT", match);
+
+ if (!entry)
+ vty_out(vty, "no match found\n");
+ else {
+ vty_out(vty, "matching entry #%"PRId64": %pFX", entry->seq,
+ &entry->prefix);
+ if (entry->ge)
+ vty_out(vty, " ge %d", entry->ge);
+ if (entry->le)
+ vty_out(vty, " le %d", entry->le);
+ vty_out(vty, "\n");
+ }
+
+ /* allow using this in scripts for quick prefix-list member tests */
+ return (ret == PREFIX_PERMIT) ? CMD_SUCCESS : CMD_WARNING;
+}
+
struct stream *prefix_bgp_orf_entry(struct stream *s, struct prefix_list *plist,
uint8_t init_flag, uint8_t permit_flag,
uint8_t deny_flag)
@@ -1537,6 +1586,7 @@ static void prefix_list_init_ipv6(void)
install_element(VIEW_NODE, &show_ipv6_prefix_list_prefix_cmd);
install_element(VIEW_NODE, &show_ipv6_prefix_list_summary_cmd);
install_element(VIEW_NODE, &show_ipv6_prefix_list_detail_cmd);
+ install_element(VIEW_NODE, &debug_prefix_list_match_cmd);
install_element(ENABLE_NODE, &clear_ipv6_prefix_list_cmd);
}
diff --git a/lib/plist.h b/lib/plist.h
index 57eb763a68..c9507df57c 100644
--- a/lib/plist.h
+++ b/lib/plist.h
@@ -37,6 +37,7 @@ enum prefix_list_type {
};
struct prefix_list;
+struct prefix_list_entry;
struct orf_prefix {
uint32_t seq;
@@ -63,12 +64,18 @@ extern struct prefix_list *prefix_list_lookup(afi_t, const char *);
*
* If no pointer is sent in, do not return anything.
* If it is a empty plist return a NULL pointer.
+ *
+ * address_mode = the "prefix" being passed in is really an address, match
+ * regardless of prefix length (i.e. ge/le are ignored.) prefix->prefixlen
+ * must be /32.
*/
extern enum prefix_list_type
-prefix_list_apply_which_prefix(struct prefix_list *plist,
- const struct prefix **which,
- const void *object);
-#define prefix_list_apply(A, B) prefix_list_apply_which_prefix((A), NULL, (B))
+prefix_list_apply_ext(struct prefix_list *plist,
+ const struct prefix_list_entry **matches,
+ union prefixconstptr prefix,
+ bool address_mode);
+#define prefix_list_apply(A, B) \
+ prefix_list_apply_ext((A), NULL, (B), false)
extern struct prefix_list *prefix_bgp_orf_lookup(afi_t, const char *);
extern struct stream *prefix_bgp_orf_entry(struct stream *,
diff --git a/lib/prefix.h b/lib/prefix.h
index bc4cb7f441..944c94f57f 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -537,20 +537,32 @@ static inline int ipv4_martian(struct in_addr *addr)
return 0;
}
-static inline int is_default_prefix(const struct prefix *p)
+static inline bool is_default_prefix4(const struct prefix_ipv4 *p)
{
- if (!p)
- return 0;
+ return p && p->family == AF_INET && p->prefixlen == 0
+ && p->prefix.s_addr == INADDR_ANY;
+}
- if ((p->family == AF_INET) && (p->u.prefix4.s_addr == INADDR_ANY)
- && (p->prefixlen == 0))
- return 1;
+static inline bool is_default_prefix6(const struct prefix_ipv6 *p)
+{
+ return p && p->family == AF_INET6 && p->prefixlen == 0
+ && memcmp(&p->prefix, &in6addr_any, sizeof(struct in6_addr))
+ == 0;
+}
- if ((p->family == AF_INET6) && (p->prefixlen == 0)
- && (!memcmp(&p->u.prefix6, &in6addr_any, sizeof(struct in6_addr))))
- return 1;
+static inline bool is_default_prefix(const struct prefix *p)
+{
+ if (p == NULL)
+ return false;
+
+ switch (p->family) {
+ case AF_INET:
+ return is_default_prefix4((const struct prefix_ipv4 *)p);
+ case AF_INET6:
+ return is_default_prefix6((const struct prefix_ipv6 *)p);
+ }
- return 0;
+ return false;
}
static inline int is_host_route(const struct prefix *p)
diff --git a/lib/route_types.txt b/lib/route_types.txt
index c48391545d..77639070c9 100644
--- a/lib/route_types.txt
+++ b/lib/route_types.txt
@@ -1,4 +1,4 @@
-# Canonical Zserv route types information registry for Quagga.
+# Canonical Zserv route types information registry for FRR.
#
# Used to construct route_types.c and route_types.h
#
@@ -60,7 +60,7 @@ ZEBRA_ROUTE_PIM, pim, pimd, 'P', 0, 0, 0, "PIM", pimd
ZEBRA_ROUTE_EIGRP, eigrp, eigrpd, 'E', 1, 0, 1, "EIGRP", eigrpd
ZEBRA_ROUTE_NHRP, nhrp, nhrpd, 'N', 1, 1, 1, "NHRP", nhrpd
# HSLS and OLSR both are AFI independent (so: 1, 1), however
-# we want to disable for them for general Quagga distribution.
+# we want to disable for them for general FRR distribution.
# This at least makes it trivial for users of these protocols
# to 'switch on' redist support (direct numeric entry remaining
# possible).
diff --git a/lib/routemap.h b/lib/routemap.h
index 4a40ec08b9..8af3b2c3c0 100644
--- a/lib/routemap.h
+++ b/lib/routemap.h
@@ -270,6 +270,7 @@ DECLARE_QOBJ_TYPE(route_map);
/* BGP route-map match conditions */
#define IS_MATCH_LOCAL_PREF(C) \
(strmatch(C, "frr-bgp-route-map:match-local-preference"))
+#define IS_MATCH_ALIAS(C) (strmatch(C, "frr-bgp-route-map:match-alias"))
#define IS_MATCH_ORIGIN(C) \
(strmatch(C, "frr-bgp-route-map:match-origin"))
#define IS_MATCH_RPKI(C) (strmatch(C, "frr-bgp-route-map:rpki"))
diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c
index bf982cfa2b..ec9033b3aa 100644
--- a/lib/routemap_cli.c
+++ b/lib/routemap_cli.c
@@ -634,6 +634,11 @@ void route_map_condition_show(struct vty *vty, struct lyd_node *dnode,
yang_dnode_get_string(
dnode,
"./rmap-match-condition/frr-bgp-route-map:local-preference"));
+ } else if (IS_MATCH_ALIAS(condition)) {
+ vty_out(vty, " match alias %s\n",
+ yang_dnode_get_string(
+ dnode,
+ "./rmap-match-condition/frr-bgp-route-map:alias"));
} else if (IS_MATCH_ORIGIN(condition)) {
vty_out(vty, " match origin %s\n",
yang_dnode_get_string(
diff --git a/lib/subdir.am b/lib/subdir.am
index 90301d800a..714af43238 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -2,7 +2,7 @@
# libfrr
#
lib_LTLIBRARIES += lib/libfrr.la
-lib_libfrr_la_LDFLAGS = -version-info 0:0:0 -Xlinker -e_libfrr_version
+lib_libfrr_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0 -Xlinker -e_libfrr_version
lib_libfrr_la_LIBADD = $(LIBCAP) $(UNWIND_LIBS) $(LIBYANG_LIBS) $(LUA_LIB) $(UST_LIBS) $(LIBM)
lib_libfrr_la_SOURCES = \
@@ -322,7 +322,7 @@ lib_LTLIBRARIES += lib/libfrrsnmp.la
endif
lib_libfrrsnmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
-lib_libfrrsnmp_la_LDFLAGS = -version-info 0:0:0
+lib_libfrrsnmp_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
lib_libfrrsnmp_la_LIBADD = $(SNMP_LIBS)
lib_libfrrsnmp_la_SOURCES = \
lib/agentx.c \
@@ -338,7 +338,7 @@ pkginclude_HEADERS += lib/resolver.h
endif
lib_libfrrcares_la_CFLAGS = $(AM_CFLAGS) $(CARES_CFLAGS)
-lib_libfrrcares_la_LDFLAGS = -version-info 0:0:0
+lib_libfrrcares_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
lib_libfrrcares_la_LIBADD = $(CARES_LIBS)
lib_libfrrcares_la_SOURCES = \
lib/resolver.c \
@@ -353,7 +353,7 @@ pkginclude_HEADERS += lib/frr_zmq.h
endif
lib_libfrrzmq_la_CFLAGS = $(AM_CFLAGS) $(ZEROMQ_CFLAGS)
-lib_libfrrzmq_la_LDFLAGS = -version-info 0:0:0
+lib_libfrrzmq_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
lib_libfrrzmq_la_LIBADD = $(ZEROMQ_LIBS)
lib_libfrrzmq_la_SOURCES = \
lib/frr_zmq.c \
@@ -367,7 +367,7 @@ module_LTLIBRARIES += lib/confd.la
endif
lib_confd_la_CFLAGS = $(AM_CFLAGS) $(CONFD_CFLAGS)
-lib_confd_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+lib_confd_la_LDFLAGS = $(MODULE_LDFLAGS)
lib_confd_la_LIBADD = lib/libfrr.la $(CONFD_LIBS)
lib_confd_la_SOURCES = lib/northbound_confd.c
@@ -379,7 +379,7 @@ module_LTLIBRARIES += lib/sysrepo.la
endif
lib_sysrepo_la_CFLAGS = $(AM_CFLAGS) $(SYSREPO_CFLAGS)
-lib_sysrepo_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+lib_sysrepo_la_LDFLAGS = $(MODULE_LDFLAGS)
lib_sysrepo_la_LIBADD = lib/libfrr.la $(SYSREPO_LIBS)
lib_sysrepo_la_SOURCES = lib/northbound_sysrepo.c
@@ -391,7 +391,7 @@ module_LTLIBRARIES += lib/grpc.la
endif
lib_grpc_la_CXXFLAGS = $(WERROR) $(GRPC_CFLAGS)
-lib_grpc_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+lib_grpc_la_LDFLAGS = $(MODULE_LDFLAGS)
lib_grpc_la_LIBADD = lib/libfrr.la grpc/libfrrgrpc_pb.la $(GRPC_LIBS)
lib_grpc_la_SOURCES = lib/northbound_grpc.cpp
@@ -419,7 +419,8 @@ lib_grammar_sandbox_LDADD = \
lib_clippy_CPPFLAGS = $(CPPFLAGS_BASE) -D_GNU_SOURCE -DBUILDING_CLIPPY
lib_clippy_CFLAGS = $(AC_CFLAGS) $(PYTHON_CFLAGS)
lib_clippy_LDADD = $(PYTHON_LIBS) $(UST_LIBS) -lelf
-lib_clippy_LDFLAGS = -export-dynamic
+# no $(SAN_FLAGS) here
+lib_clippy_LDFLAGS = -export-dynamic $(AC_LDFLAGS) $(AC_LDFLAGS_EXEC)
lib_clippy_SOURCES = \
lib/jhash.c \
lib/clippy.c \
diff --git a/mlag/subdir.am b/mlag/subdir.am
index 49d1761505..376eea8bc9 100644
--- a/mlag/subdir.am
+++ b/mlag/subdir.am
@@ -2,7 +2,7 @@ if HAVE_PROTOBUF3
lib_LTLIBRARIES += mlag/libmlag_pb.la
endif
-mlag_libmlag_pb_la_LDFLAGS = -version-info 0:0:0
+mlag_libmlag_pb_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
mlag_libmlag_pb_la_CPPFLAGS = $(AM_CPPFLAGS) $(PROTOBUF_C_CFLAGS)
mlag_libmlag_pb_la_SOURCES = \
# end
diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c
index f289bf26b9..9dd232dae5 100644
--- a/ospf6d/ospf6_abr.c
+++ b/ospf6d/ospf6_abr.c
@@ -477,11 +477,11 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route,
monotime(&summary->changed);
}
+ summary->prefix_options = route->prefix_options;
summary->path.router_bits = route->path.router_bits;
summary->path.options[0] = route->path.options[0];
summary->path.options[1] = route->path.options[1];
summary->path.options[2] = route->path.options[2];
- summary->path.prefix_options = route->path.prefix_options;
summary->path.area_id = area->area_id;
summary->path.type = OSPF6_PATH_TYPE_INTER;
summary->path.subtype = route->path.subtype;
@@ -514,7 +514,7 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route,
/* Fill Inter-Area-Prefix-LSA */
OSPF6_ABR_SUMMARY_METRIC_SET(prefix_lsa, route->path.cost);
prefix_lsa->prefix.prefix_length = route->prefix.prefixlen;
- prefix_lsa->prefix.prefix_options = route->path.prefix_options;
+ prefix_lsa->prefix.prefix_options = route->prefix_options;
/* set Prefix */
memcpy(p, &route->prefix.u.prefix6,
@@ -710,24 +710,11 @@ void ospf6_abr_defaults_to_stub(struct ospf6 *o)
struct listnode *node, *nnode;
struct ospf6_area *oa;
struct ospf6_route *def, *route;
- struct ospf6_redist *red;
int type = DEFAULT_ROUTE;
- struct prefix_ipv6 p = {};
if (!o->backbone)
return;
- red = ospf6_redist_lookup(o, type, 0);
- if (!red)
- return;
-
- p.family = AF_INET6;
- p.prefixlen = 0;
-
- route = ospf6_route_lookup((struct prefix *)&p, o->external_table);
- if (!route)
- return;
-
def = ospf6_route_create();
def->type = OSPF6_DEST_TYPE_NETWORK;
def->prefix.family = AF_INET6;
@@ -1167,6 +1154,7 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
route->type = type;
route->prefix = prefix;
+ route->prefix_options = prefix_options;
route->path.origin.type = lsa->header->type;
route->path.origin.id = lsa->header->id;
route->path.origin.adv_router = lsa->header->adv_router;
@@ -1174,7 +1162,6 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
route->path.options[0] = options[0];
route->path.options[1] = options[1];
route->path.options[2] = options[2];
- route->path.prefix_options = prefix_options;
route->path.area_id = oa->area_id;
route->path.type = OSPF6_PATH_TYPE_INTER;
route->path.cost = abr_entry->path.cost + cost;
diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c
index aad4e7571d..84111e4b7d 100644
--- a/ospf6d/ospf6_asbr.c
+++ b/ospf6d/ospf6_asbr.c
@@ -121,7 +121,7 @@ void ospf6_as_external_lsa_originate(struct ospf6_route *route,
as_external_lsa->prefix.prefix_length = route->prefix.prefixlen;
/* PrefixOptions */
- as_external_lsa->prefix.prefix_options = route->path.prefix_options;
+ as_external_lsa->prefix.prefix_options = route->prefix_options;
/* don't use refer LS-type */
as_external_lsa->prefix.prefix_refer_lstype = htons(0);
@@ -589,12 +589,12 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa)
route->prefix.prefixlen = external->prefix.prefix_length;
ospf6_prefix_in6_addr(&route->prefix.u.prefix6, external,
&external->prefix);
+ route->prefix_options = external->prefix.prefix_options;
route->path.area_id = asbr_entry->path.area_id;
route->path.origin.type = lsa->header->type;
route->path.origin.id = lsa->header->id;
route->path.origin.adv_router = lsa->header->adv_router;
- route->path.prefix_options = external->prefix.prefix_options;
memcpy(&route->path.ls_prefix, &asbr_id, sizeof(struct prefix));
if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_E)) {
@@ -2568,36 +2568,41 @@ int config_write_ospf6_debug_asbr(struct vty *vty)
return 0;
}
-int ospf6_distribute_config_write(struct vty *vty, struct ospf6 *ospf6)
+static void ospf6_default_originate_write(struct vty *vty, struct ospf6 *o)
{
struct ospf6_redist *red;
- if (ospf6) {
- /* default-route print. */
- if (ospf6->default_originate != DEFAULT_ORIGINATE_NONE) {
- vty_out(vty, " default-information originate");
- if (ospf6->default_originate
- == DEFAULT_ORIGINATE_ALWAYS)
- vty_out(vty, " always");
-
- red = ospf6_redist_lookup(ospf6, DEFAULT_ROUTE, 0);
- if (red) {
- if (red->dmetric.value >= 0)
- vty_out(vty, " metric %d",
- red->dmetric.value);
-
- if (red->dmetric.type >= 0)
- vty_out(vty, " metric-type %d",
- red->dmetric.type);
-
- if (ROUTEMAP_NAME(red))
- vty_out(vty, " route-map %s",
- ROUTEMAP_NAME(red));
- }
+ vty_out(vty, " default-information originate");
+ if (o->default_originate == DEFAULT_ORIGINATE_ALWAYS)
+ vty_out(vty, " always");
- vty_out(vty, "\n");
- }
+ red = ospf6_redist_lookup(o, DEFAULT_ROUTE, 0);
+ if (red == NULL) {
+ vty_out(vty, "\n");
+ return;
}
+
+ if (red->dmetric.value >= 0)
+ vty_out(vty, " metric %d", red->dmetric.value);
+
+ if (red->dmetric.type >= 0)
+ vty_out(vty, " metric-type %d", red->dmetric.type);
+
+ if (ROUTEMAP_NAME(red))
+ vty_out(vty, " route-map %s", ROUTEMAP_NAME(red));
+
+ vty_out(vty, "\n");
+}
+
+int ospf6_distribute_config_write(struct vty *vty, struct ospf6 *o)
+{
+ if (o == NULL)
+ return 0;
+
+ /* Print default originate configuration. */
+ if (o->default_originate != DEFAULT_ORIGINATE_NONE)
+ ospf6_default_originate_write(vty, o);
+
return 0;
}
diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c
index 738c2218fa..0a384a98e6 100644
--- a/ospf6d/ospf6_flood.c
+++ b/ospf6d/ospf6_flood.c
@@ -89,6 +89,16 @@ void ospf6_lsa_originate(struct ospf6_lsa *lsa)
struct ospf6_lsa *old;
struct ospf6_lsdb *lsdb_self;
+ if (lsa->header->adv_router == INADDR_ANY) {
+ if (IS_OSPF6_DEBUG_ORIGINATE_TYPE(lsa->header->type))
+ zlog_debug(
+ "Refusing to originate LSA (zero router ID): %s",
+ lsa->name);
+
+ ospf6_lsa_delete(lsa);
+ return;
+ }
+
/* find previous LSA */
old = ospf6_lsdb_lookup(lsa->header->type, lsa->header->id,
lsa->header->adv_router, lsa->lsdb);
diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c
index 1e75fc60f6..468a4b8e81 100644
--- a/ospf6d/ospf6_interface.c
+++ b/ospf6d/ospf6_interface.c
@@ -385,7 +385,6 @@ void ospf6_interface_connected_route_update(struct interface *ifp)
struct connected *c;
struct listnode *node, *nnode;
struct in6_addr nh_addr;
- int count = 0, max_addr_count;
oi = (struct ospf6_interface *)ifp->info;
if (oi == NULL)
@@ -404,22 +403,10 @@ void ospf6_interface_connected_route_update(struct interface *ifp)
/* update "route to advertise" interface route table */
ospf6_route_remove_all(oi->route_connected);
- if (oi->ifmtu >= OSPF6_JUMBO_MTU)
- max_addr_count = OSPF6_MAX_IF_ADDRS_JUMBO;
- else
- max_addr_count = OSPF6_MAX_IF_ADDRS;
-
for (ALL_LIST_ELEMENTS(oi->interface->connected, node, nnode, c)) {
if (c->address->family != AF_INET6)
continue;
- /* number of interface addresses supported is based on MTU
- * size of OSPFv3 packet
- */
- count++;
- if (count >= max_addr_count)
- break;
-
CONTINUE_IF_ADDRESS_LINKLOCAL(IS_OSPF6_DEBUG_INTERFACE,
c->address);
CONTINUE_IF_ADDRESS_UNSPECIFIED(IS_OSPF6_DEBUG_INTERFACE,
@@ -680,6 +667,43 @@ static uint8_t dr_election(struct ospf6_interface *oi)
return next_state;
}
+#ifdef __FreeBSD__
+
+#include <ifaddrs.h>
+
+static bool ifmaddr_check(ifindex_t ifindex, struct in6_addr *addr)
+{
+ struct ifmaddrs *ifmap, *ifma;
+ struct sockaddr_dl *sdl;
+ struct sockaddr_in6 *sin6;
+ bool found = false;
+
+ if (getifmaddrs(&ifmap) != 0)
+ return false;
+
+ for (ifma = ifmap; ifma; ifma = ifma->ifma_next) {
+ if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL)
+ continue;
+ if (ifma->ifma_name->sa_family != AF_LINK)
+ continue;
+ if (ifma->ifma_addr->sa_family != AF_INET6)
+ continue;
+ sdl = (struct sockaddr_dl *)ifma->ifma_name;
+ sin6 = (struct sockaddr_in6 *)ifma->ifma_addr;
+ if (sdl->sdl_index == ifindex
+ && memcmp(&sin6->sin6_addr, addr, IPV6_MAX_BYTELEN) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (ifmap)
+ freeifmaddrs(ifmap);
+
+ return found;
+}
+
+#endif /* __FreeBSD__ */
/* Interface State Machine */
int interface_up(struct thread *thread)
@@ -693,11 +717,7 @@ int interface_up(struct thread *thread)
if (!oi->type_cfg)
oi->type = ospf6_default_iftype(oi->interface);
- /*
- * Remove old pointer. If this thread wasn't a timer this
- * operation won't make a difference, because it is already NULL.
- */
- oi->thread_sso = NULL;
+ thread_cancel(&oi->thread_sso);
if (IS_OSPF6_DEBUG_INTERFACE)
zlog_debug("Interface Event %s: [InterfaceUp]",
@@ -733,20 +753,24 @@ int interface_up(struct thread *thread)
/* If no area assigned, return */
if (oi->area == NULL) {
zlog_warn(
- "%s: Not scheduleing Hello for %s as there is no area assigned yet",
+ "%s: Not scheduling Hello for %s as there is no area assigned yet",
__func__, oi->interface->name);
return 0;
}
#ifdef __FreeBSD__
/*
- * XXX: Schedule IPv6 group join for later, otherwise we might
- * lose the multicast group registration caused by IPv6 group
- * leave race.
+ * There's a delay in FreeBSD between issuing a command to leave a
+ * multicast group and an actual leave. If we execute "no router ospf6"
+ * and "router ospf6" fast enough, we can end up in a situation when OS
+ * performs the leave later than it performs the join and the interface
+ * remains without a multicast group. We have to do the join only after
+ * the interface actually left the group.
*/
- if (oi->sso_try_cnt == 0) {
- oi->sso_try_cnt++;
- zlog_info("Scheduling %s for sso", oi->interface->name);
+ if (ifmaddr_check(oi->interface->ifindex, &allspfrouters6)) {
+ zlog_info(
+ "Interface %s is still in all routers group, rescheduling for SSO",
+ oi->interface->name);
thread_add_timer(master, interface_up, oi,
OSPF6_INTERFACE_SSO_RETRY_INT,
&oi->thread_sso);
@@ -784,7 +808,9 @@ int interface_up(struct thread *thread)
}
/* decide next interface state */
- if (oi->type == OSPF_IFTYPE_POINTOPOINT) {
+ if (oi->type == OSPF_IFTYPE_LOOPBACK) {
+ ospf6_interface_state_change(OSPF6_INTERFACE_LOOPBACK, oi);
+ } else if (oi->type == OSPF_IFTYPE_POINTOPOINT) {
ospf6_interface_state_change(OSPF6_INTERFACE_POINTTOPOINT, oi);
} else if (oi->priority == 0)
ospf6_interface_state_change(OSPF6_INTERFACE_DROTHER, oi);
@@ -1691,7 +1717,6 @@ DEFUN (ipv6_ospf6_area,
int idx_ipv4 = 3;
uint32_t area_id;
int format;
- int ipv6_count = 0;
assert(ifp);
@@ -1706,23 +1731,6 @@ DEFUN (ipv6_ospf6_area,
return CMD_SUCCESS;
}
- /* if more than OSPF6_MAX_IF_ADDRS are configured on this interface
- * then don't allow ospfv3 to be configured
- */
- ipv6_count = connected_count_by_family(ifp, AF_INET6);
- if (oi->ifmtu == OSPF6_DEFAULT_MTU && ipv6_count > OSPF6_MAX_IF_ADDRS) {
- vty_out(vty,
- "can not configure OSPFv3 on if %s, must have less than %d interface addresses but has %d addresses\n",
- ifp->name, OSPF6_MAX_IF_ADDRS, ipv6_count);
- return CMD_WARNING_CONFIG_FAILED;
- } else if (oi->ifmtu >= OSPF6_JUMBO_MTU
- && ipv6_count > OSPF6_MAX_IF_ADDRS_JUMBO) {
- vty_out(vty,
- "can not configure OSPFv3 on if %s, must have less than %d interface addresses but has %d addresses\n",
- ifp->name, OSPF6_MAX_IF_ADDRS_JUMBO, ipv6_count);
- return CMD_WARNING_CONFIG_FAILED;
- }
-
if (str2area_id(argv[idx_ipv4]->arg, &area_id, &format)) {
vty_out(vty, "Malformed Area-ID: %s\n", argv[idx_ipv4]->arg);
return CMD_WARNING_CONFIG_FAILED;
diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h
index c9cd74b691..b5efca743e 100644
--- a/ospf6d/ospf6_interface.h
+++ b/ospf6d/ospf6_interface.h
@@ -201,7 +201,6 @@ extern void ospf6_interface_disable(struct ospf6_interface *);
extern void ospf6_interface_state_update(struct interface *);
extern void ospf6_interface_connected_route_update(struct interface *);
-extern void ospf6_interface_connected_route_add(struct connected *);
extern struct in6_addr *
ospf6_interface_get_global_address(struct interface *ifp);
diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c
index c971c6180e..06f64bbc40 100644
--- a/ospf6d/ospf6_intra.c
+++ b/ospf6d/ospf6_intra.c
@@ -767,7 +767,6 @@ int ospf6_link_lsa_originate(struct thread *thread)
struct ospf6_link_lsa *link_lsa;
struct ospf6_route *route;
struct ospf6_prefix *op;
- int count, max_addr_count;
oi = (struct ospf6_interface *)THREAD_ARG(thread);
oi->thread_link_lsa = NULL;
@@ -811,30 +810,22 @@ int ospf6_link_lsa_originate(struct thread *thread)
memcpy(link_lsa->options, oi->area->options, 3);
memcpy(&link_lsa->linklocal_addr, oi->linklocal_addr,
sizeof(struct in6_addr));
+ link_lsa->prefix_num = htonl(oi->route_connected->count);
op = (struct ospf6_prefix *)((caddr_t)link_lsa
+ sizeof(struct ospf6_link_lsa));
- /* connected prefix to advertise, number of interface addresses
- * supported is based on MTU size of OSPFv3 packets
- */
- if (oi->ifmtu >= OSPF6_JUMBO_MTU)
- max_addr_count = OSPF6_MAX_IF_ADDRS_JUMBO;
- else
- max_addr_count = OSPF6_MAX_IF_ADDRS;
- for (route = ospf6_route_head(oi->route_connected), count = 0;
- route && count < max_addr_count;
- route = ospf6_route_next(route), count++) {
+ /* connected prefix to advertise */
+ for (route = ospf6_route_head(oi->route_connected); route;
+ route = ospf6_route_next(route)) {
op->prefix_length = route->prefix.prefixlen;
- op->prefix_options = route->path.prefix_options;
+ op->prefix_options = route->prefix_options;
op->prefix_metric = htons(0);
memcpy(OSPF6_PREFIX_BODY(op), &route->prefix.u.prefix6,
OSPF6_PREFIX_SPACE(op->prefix_length));
op = OSPF6_PREFIX_NEXT(op);
}
- link_lsa->prefix_num = htonl(count);
-
/* Fill LSA Header */
lsa_header->age = 0;
lsa_header->type = htons(OSPF6_LSTYPE_LINK);
@@ -1014,7 +1005,6 @@ int ospf6_intra_prefix_lsa_originate_stub(struct thread *thread)
unsigned short prefix_num = 0;
struct ospf6_route_table *route_advertise;
int ls_id = 0;
- int count, max_addr_count;
oa = (struct ospf6_area *)THREAD_ARG(thread);
oa->thread_intra_prefix_lsa = NULL;
@@ -1060,8 +1050,6 @@ int ospf6_intra_prefix_lsa_originate_stub(struct thread *thread)
intra_prefix_lsa->ref_adv_router = oa->ospf6->router_id;
route_advertise = ospf6_route_table_create(0, 0);
- route_advertise->hook_add = NULL;
- route_advertise->hook_remove = NULL;
for (ALL_LIST_ELEMENTS_RO(oa->if_list, i, oi)) {
if (oi->state == OSPF6_INTERFACE_DOWN) {
@@ -1090,14 +1078,8 @@ int ospf6_intra_prefix_lsa_originate_stub(struct thread *thread)
zlog_debug(" Interface %s:", oi->interface->name);
/* connected prefix to advertise */
- if (oi->ifmtu >= OSPF6_JUMBO_MTU)
- max_addr_count = OSPF6_MAX_IF_ADDRS_JUMBO;
- else
- max_addr_count = OSPF6_MAX_IF_ADDRS;
-
- for (route = ospf6_route_head(oi->route_connected), count = 0;
- route && count < max_addr_count;
- route = ospf6_route_best_next(route), count++) {
+ for (route = ospf6_route_head(oi->route_connected); route;
+ route = ospf6_route_best_next(route)) {
if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX))
zlog_debug(" include %pFX", &route->prefix);
ospf6_route_add(ospf6_route_copy(route),
@@ -1193,7 +1175,7 @@ int ospf6_intra_prefix_lsa_originate_stub(struct thread *thread)
}
op->prefix_length = route->prefix.prefixlen;
- op->prefix_options = route->path.prefix_options;
+ op->prefix_options = route->prefix_options;
op->prefix_metric = htons(route->path.cost);
memcpy(OSPF6_PREFIX_BODY(op), &route->prefix.u.prefix6,
OSPF6_PREFIX_SPACE(op->prefix_length));
@@ -1312,8 +1294,6 @@ int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread)
/* connected prefix to advertise */
route_advertise = ospf6_route_table_create(0, 0);
- route_advertise->hook_add = NULL;
- route_advertise->hook_remove = NULL;
type = ntohs(OSPF6_LSTYPE_LINK);
for (ALL_LSDB_TYPED(oi->lsdb, type, lsa)) {
@@ -1356,6 +1336,7 @@ int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread)
sizeof(struct in6_addr));
memcpy(&route->prefix.u.prefix6, OSPF6_PREFIX_BODY(op),
OSPF6_PREFIX_SPACE(op->prefix_length));
+ route->prefix_options = op->prefix_options;
route->path.origin.type = lsa->header->type;
route->path.origin.id = lsa->header->id;
@@ -1363,7 +1344,6 @@ int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread)
route->path.options[0] = link_lsa->options[0];
route->path.options[1] = link_lsa->options[1];
route->path.options[2] = link_lsa->options[2];
- route->path.prefix_options = op->prefix_options;
route->path.area_id = oi->area->area_id;
route->path.type = OSPF6_PATH_TYPE_INTRA;
@@ -1384,7 +1364,7 @@ int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread)
for (route = ospf6_route_head(route_advertise); route;
route = ospf6_route_best_next(route)) {
op->prefix_length = route->prefix.prefixlen;
- op->prefix_options = route->path.prefix_options;
+ op->prefix_options = route->prefix_options;
op->prefix_metric = htons(0);
memcpy(OSPF6_PREFIX_BODY(op), &route->prefix.u.prefix6,
OSPF6_PREFIX_SPACE(op->prefix_length));
@@ -1817,12 +1797,12 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa)
route->prefix.prefixlen = op->prefix_length;
ospf6_prefix_in6_addr(&route->prefix.u.prefix6,
intra_prefix_lsa, op);
+ route->prefix_options = op->prefix_options;
route->type = OSPF6_DEST_TYPE_NETWORK;
route->path.origin.type = lsa->header->type;
route->path.origin.id = lsa->header->id;
route->path.origin.adv_router = lsa->header->adv_router;
- route->path.prefix_options = op->prefix_options;
route->path.area_id = oa->area_id;
route->path.type = OSPF6_PATH_TYPE_INTRA;
route->path.metric_type = 1;
diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c
index f5b06ef2d7..549f5668b9 100644
--- a/ospf6d/ospf6_message.c
+++ b/ospf6d/ospf6_message.c
@@ -1977,7 +1977,6 @@ static int ospf6_write(struct thread *thread)
__func__, latency);
oi->last_hello = timestamp;
oi->hello_out++;
- ospf6_hello_print(oh, OSPF6_ACTION_SEND);
break;
case OSPF6_MESSAGE_TYPE_DBDESC:
oi->db_desc_out++;
@@ -2370,7 +2369,19 @@ static void ospf6_send_lsupdate(struct ospf6_neighbor *on,
}
if (oi) {
ospf6_packet_add(oi, op);
- OSPF6_MESSAGE_WRITE_ON(oi);
+ /* If ospf instance is being deleted, send the packet
+ * immediately
+ */
+ if ((oi->area == NULL) || (oi->area->ospf6 == NULL))
+ return;
+ if (oi->area->ospf6->inst_shutdown) {
+ if (oi->on_write_q == 0) {
+ listnode_add(oi->area->ospf6->oi_write_q, oi);
+ oi->on_write_q = 1;
+ }
+ thread_execute(master, ospf6_write, oi->area->ospf6, 0);
+ } else
+ OSPF6_MESSAGE_WRITE_ON(oi);
}
}
diff --git a/ospf6d/ospf6_nssa.c b/ospf6d/ospf6_nssa.c
index 9f8cdf8fb7..bd6fb308dd 100644
--- a/ospf6d/ospf6_nssa.c
+++ b/ospf6d/ospf6_nssa.c
@@ -1296,7 +1296,7 @@ void ospf6_nssa_lsa_originate(struct ospf6_route *route,
as_external_lsa->prefix.prefix_length = route->prefix.prefixlen;
/* PrefixOptions */
- as_external_lsa->prefix.prefix_options = route->path.prefix_options;
+ as_external_lsa->prefix.prefix_options = route->prefix_options;
/* Set the P bit */
as_external_lsa->prefix.prefix_options |= OSPF6_PREFIX_OPTION_P;
diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c
index 0a026785f4..80f0e7d26b 100644
--- a/ospf6d/ospf6_route.c
+++ b/ospf6d/ospf6_route.c
@@ -436,6 +436,7 @@ struct ospf6_route *ospf6_route_copy(struct ospf6_route *route)
new = ospf6_route_create();
new->type = route->type;
memcpy(&new->prefix, &route->prefix, sizeof(struct prefix));
+ new->prefix_options = route->prefix_options;
new->installed = route->installed;
new->changed = route->changed;
new->flag = route->flag;
@@ -1137,6 +1138,7 @@ void ospf6_route_show_detail(struct vty *vty, struct ospf6_route *route,
{
char destination[PREFIX2STR_BUFFER], nexthop[64];
char area_id[16], id[16], adv_router[16], capa[16], options[16];
+ char pfx_options[16];
struct timeval now, res;
char duration[64];
struct listnode *node;
@@ -1264,10 +1266,13 @@ void ospf6_route_show_detail(struct vty *vty, struct ospf6_route *route,
vty_out(vty, "Router Bits: %s\n", capa);
/* Prefix Options */
+ ospf6_prefix_options_printbuf(route->prefix_options, pfx_options,
+ sizeof(pfx_options));
if (use_json)
- json_object_string_add(json_route, "prefixOptions", "xxx");
+ json_object_string_add(json_route, "prefixOptions",
+ pfx_options);
else
- vty_out(vty, "Prefix Options: xxx\n");
+ vty_out(vty, "Prefix Options: %s\n", pfx_options);
/* Metrics */
if (use_json) {
diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h
index a791a82cd4..ecfb45d1ea 100644
--- a/ospf6d/ospf6_route.h
+++ b/ospf6d/ospf6_route.h
@@ -79,9 +79,6 @@ struct ospf6_path {
/* Optional Capabilities */
uint8_t options[3];
- /* Prefix Options */
- uint8_t prefix_options;
-
/* Associated Area */
in_addr_t area_id;
@@ -147,6 +144,9 @@ struct ospf6_route {
/* flag */
uint8_t flag;
+ /* Prefix Options */
+ uint8_t prefix_options;
+
/* route option */
void *route_option;
diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c
index 032484e288..051b3a63ef 100644
--- a/ospf6d/ospf6_spf.c
+++ b/ospf6d/ospf6_spf.c
@@ -225,21 +225,25 @@ static char *ospf6_lsdesc_backlink(struct ospf6_lsa *lsa, caddr_t lsdesc,
assert(!(OSPF6_LSA_IS_TYPE(NETWORK, lsa)
&& VERTEX_IS_TYPE(NETWORK, v)));
- if (OSPF6_LSA_IS_TYPE(NETWORK, lsa)
- && NETWORK_LSDESC_GET_NBR_ROUTERID(backlink)
- == v->lsa->header->adv_router)
- found = backlink;
- else if (VERTEX_IS_TYPE(NETWORK, v)
- && ROUTER_LSDESC_IS_TYPE(TRANSIT_NETWORK, backlink)
- && ROUTER_LSDESC_GET_NBR_ROUTERID(backlink)
- == v->lsa->header->adv_router
- && ROUTER_LSDESC_GET_NBR_IFID(backlink)
- == ntohl(v->lsa->header->id))
- found = backlink;
- else {
+ if (OSPF6_LSA_IS_TYPE(NETWORK, lsa)) {
+ if (NETWORK_LSDESC_GET_NBR_ROUTERID(backlink)
+ == v->lsa->header->adv_router)
+ found = backlink;
+ } else if (VERTEX_IS_TYPE(NETWORK, v)) {
+ if (ROUTER_LSDESC_IS_TYPE(TRANSIT_NETWORK, backlink)
+ && ROUTER_LSDESC_GET_NBR_ROUTERID(backlink)
+ == v->lsa->header->adv_router
+ && ROUTER_LSDESC_GET_NBR_IFID(backlink)
+ == ntohl(v->lsa->header->id))
+ found = backlink;
+ } else {
+ assert(OSPF6_LSA_IS_TYPE(ROUTER, lsa)
+ && VERTEX_IS_TYPE(ROUTER, v));
+
if (!ROUTER_LSDESC_IS_TYPE(POINTTOPOINT, backlink)
|| !ROUTER_LSDESC_IS_TYPE(POINTTOPOINT, lsdesc))
continue;
+
if (ROUTER_LSDESC_GET_NBR_IFID(backlink)
!= ROUTER_LSDESC_GET_IFID(lsdesc)
|| ROUTER_LSDESC_GET_NBR_IFID(lsdesc)
diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c
index 6f40989efd..92f1e50c65 100644
--- a/ospf6d/ospf6_top.c
+++ b/ospf6d/ospf6_top.c
@@ -989,7 +989,6 @@ DEFUN_HIDDEN (ospf6_interface_area,
struct ospf6_interface *oi;
struct interface *ifp;
vrf_id_t vrf_id = VRF_DEFAULT;
- int ipv6_count = 0;
uint32_t area_id;
int format;
@@ -1012,23 +1011,6 @@ DEFUN_HIDDEN (ospf6_interface_area,
return CMD_SUCCESS;
}
- /* if more than OSPF6_MAX_IF_ADDRS are configured on this interface
- * then don't allow ospfv3 to be configured
- */
- ipv6_count = connected_count_by_family(ifp, AF_INET6);
- if (oi->ifmtu == OSPF6_DEFAULT_MTU && ipv6_count > OSPF6_MAX_IF_ADDRS) {
- vty_out(vty,
- "can not configure OSPFv3 on if %s, must have less than %d interface addresses but has %d addresses\n",
- ifp->name, OSPF6_MAX_IF_ADDRS, ipv6_count);
- return CMD_WARNING_CONFIG_FAILED;
- } else if (oi->ifmtu >= OSPF6_JUMBO_MTU
- && ipv6_count > OSPF6_MAX_IF_ADDRS_JUMBO) {
- vty_out(vty,
- "can not configure OSPFv3 on if %s, must have less than %d interface addresses but has %d addresses\n",
- ifp->name, OSPF6_MAX_IF_ADDRS_JUMBO, ipv6_count);
- return CMD_WARNING_CONFIG_FAILED;
- }
-
if (str2area_id(argv[idx_ipv4]->arg, &area_id, &format)) {
vty_out(vty, "Malformed Area-ID: %s\n", argv[idx_ipv4]->arg);
return CMD_WARNING_CONFIG_FAILED;
diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h
index 3eb423f681..b546ee87ae 100644
--- a/ospf6d/ospf6_top.h
+++ b/ospf6d/ospf6_top.h
@@ -164,10 +164,6 @@ DECLARE_QOBJ_TYPE(ospf6);
#define OSPF6_DISABLED 0x01
#define OSPF6_STUB_ROUTER 0x02
-#define OSPF6_MAX_IF_ADDRS 100
-#define OSPF6_MAX_IF_ADDRS_JUMBO 200
-#define OSPF6_DEFAULT_MTU 1500
-#define OSPF6_JUMBO_MTU 9000
/* global pointer for OSPF top data structure */
extern struct ospf6 *ospf6;
diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c
index a7e15c68ae..5403e643dc 100644
--- a/ospf6d/ospf6_zebra.c
+++ b/ospf6d/ospf6_zebra.c
@@ -131,38 +131,17 @@ void ospf6_zebra_no_redistribute(int type, vrf_id_t vrf_id)
static int ospf6_zebra_if_address_update_add(ZAPI_CALLBACK_ARGS)
{
struct connected *c;
- struct ospf6_interface *oi;
- int ipv6_count = 0;
c = zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD,
zclient->ibuf, vrf_id);
if (c == NULL)
return 0;
- oi = (struct ospf6_interface *)c->ifp->info;
- if (oi == NULL)
- oi = ospf6_interface_create(c->ifp);
- assert(oi);
-
if (IS_OSPF6_DEBUG_ZEBRA(RECV))
zlog_debug("Zebra Interface address add: %s %5s %pFX",
c->ifp->name, prefix_family_str(c->address),
c->address);
- ipv6_count = connected_count_by_family(c->ifp, AF_INET6);
- if (oi->ifmtu == OSPF6_DEFAULT_MTU && ipv6_count > OSPF6_MAX_IF_ADDRS) {
- zlog_warn(
- "Zebra Interface : %s has too many interface addresses %d only support %d, increase MTU",
- c->ifp->name, ipv6_count, OSPF6_MAX_IF_ADDRS);
- return 0;
- } else if (oi->ifmtu >= OSPF6_JUMBO_MTU
- && ipv6_count > OSPF6_MAX_IF_ADDRS_JUMBO) {
- zlog_warn(
- "Zebra Interface : %s has too many interface addresses %d only support %d",
- c->ifp->name, ipv6_count, OSPF6_MAX_IF_ADDRS_JUMBO);
- return 0;
- }
-
if (c->address->family == AF_INET6) {
ospf6_interface_state_update(c->ifp);
ospf6_interface_connected_route_update(c->ifp);
@@ -194,16 +173,6 @@ static int ospf6_zebra_if_address_update_delete(ZAPI_CALLBACK_ARGS)
return 0;
}
-static int is_prefix_default(struct prefix_ipv6 *p)
-{
- struct prefix_ipv6 q = {};
-
- q.family = AF_INET6;
- q.prefixlen = 0;
-
- return prefix_same((struct prefix *)p, (struct prefix *)&q);
-}
-
static int ospf6_zebra_read_route(ZAPI_CALLBACK_ARGS)
{
struct zapi_route api;
@@ -239,7 +208,7 @@ static int ospf6_zebra_read_route(ZAPI_CALLBACK_ARGS)
ifindex, api.tag);
memcpy(&p, &api.prefix, sizeof(p));
- if (is_prefix_default(&p))
+ if (is_default_prefix6(&p))
api.type = DEFAULT_ROUTE;
if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am
index 2b7bce5392..3f9ff76f3b 100644
--- a/ospf6d/subdir.am
+++ b/ospf6d/subdir.am
@@ -84,7 +84,7 @@ ospf6d_ospf6d_SOURCES = \
ospf6d_ospf6d_snmp_la_SOURCES = ospf6d/ospf6_snmp.c
ospf6d_ospf6d_snmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
-ospf6d_ospf6d_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+ospf6d_ospf6d_snmp_la_LDFLAGS = $(MODULE_LDFLAGS)
ospf6d_ospf6d_snmp_la_LIBADD = lib/libfrrsnmp.la
clippy_scan += \
diff --git a/ospfclient/subdir.am b/ospfclient/subdir.am
index 756ad88f15..1f9547ab87 100644
--- a/ospfclient/subdir.am
+++ b/ospfclient/subdir.am
@@ -8,7 +8,7 @@ noinst_PROGRAMS += ospfclient/ospfclient
#man8 += $(MANBUILD)/frr-ospfclient.8
endif
-ospfclient_libfrrospfapiclient_la_LDFLAGS = -version-info 0:0:0
+ospfclient_libfrrospfapiclient_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
ospfclient_libfrrospfapiclient_la_LIBADD = lib/libfrr.la
ospfclient_libfrrospfapiclient_la_SOURCES = \
ospfclient/ospf_apiclient.c \
diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c
index a6027ee9d1..6a009d2144 100644
--- a/ospfd/ospf_abr.c
+++ b/ospfd/ospf_abr.c
@@ -1694,6 +1694,9 @@ static void ospf_abr_manage_discard_routes(struct ospf *ospf)
static void ospf_abr_nssa_task(struct ospf *ospf) /* called only if any_nssa */
{
+ if (ospf->gr_info.restart_in_progress)
+ return;
+
if (IS_DEBUG_OSPF_NSSA)
zlog_debug("Check for NSSA-ABR Tasks():");
@@ -1758,6 +1761,9 @@ static void ospf_abr_nssa_task(struct ospf *ospf) /* called only if any_nssa */
summary-LSA origination and flooding. */
void ospf_abr_task(struct ospf *ospf)
{
+ if (ospf->gr_info.restart_in_progress)
+ return;
+
if (IS_DEBUG_OSPF_EVENT)
zlog_debug("ospf_abr_task(): Start");
diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c
index 192dbe4fc8..53d2ec538c 100644
--- a/ospfd/ospf_asbr.c
+++ b/ospfd/ospf_asbr.c
@@ -334,7 +334,7 @@ void ospf_redistribute_withdraw(struct ospf *ospf, uint8_t type,
struct ospf_external_aggr_rt *aggr;
- if (is_prefix_default(&ei->p)
+ if (is_default_prefix4(&ei->p)
&& ospf->default_originate != DEFAULT_ORIGINATE_NONE)
continue;
@@ -862,7 +862,7 @@ static void ospf_handle_external_aggr_add(struct ospf *ospf)
continue;
ei = rn->info;
- if (is_prefix_default(&ei->p))
+ if (is_default_prefix4(&ei->p))
continue;
/* Check the AS-external-LSA
diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c
index 51da4a55a7..aaacebca14 100644
--- a/ospfd/ospf_ase.c
+++ b/ospfd/ospf_ase.c
@@ -609,6 +609,16 @@ static int ospf_ase_calculate_timer(struct thread *t)
+ (stop_time.tv_usec
- start_time.tv_usec));
}
+
+ /*
+ * Uninstall remnant routes that were installed before the restart, but
+ * that are no longer valid.
+ */
+ if (ospf->gr_info.finishing_restart) {
+ ospf_zebra_gr_disable(ospf);
+ ospf->gr_info.finishing_restart = false;
+ }
+
return 0;
}
diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c
index e490070d03..f11c84b092 100644
--- a/ospfd/ospf_dump.c
+++ b/ospfd/ospf_dump.c
@@ -1552,21 +1552,16 @@ DEFUN(no_debug_ospf_ldp_sync,
return CMD_SUCCESS;
}
-DEFPY (debug_ospf_gr,
- debug_ospf_gr_cmd,
- "[no$no] debug ospf graceful-restart helper",
- NO_STR
- DEBUG_STR OSPF_STR
- "Gracefull restart\n"
- "Helper Information\n")
+DEFPY(debug_ospf_gr, debug_ospf_gr_cmd, "[no$no] debug ospf graceful-restart",
+ NO_STR DEBUG_STR OSPF_STR "OSPF Graceful Restart\n")
{
if (vty->node == CONFIG_NODE)
- CONF_DEBUG_ON(gr, GR_HELPER);
+ CONF_DEBUG_ON(gr, GR);
if (!no)
- TERM_DEBUG_ON(gr, GR_HELPER);
+ TERM_DEBUG_ON(gr, GR);
else
- TERM_DEBUG_OFF(gr, GR_HELPER);
+ TERM_DEBUG_OFF(gr, GR);
return CMD_SUCCESS;
}
@@ -1764,9 +1759,9 @@ static int show_debugging_ospf_common(struct vty *vty)
if (IS_DEBUG_OSPF(ldp_sync, LDP_SYNC) == OSPF_DEBUG_LDP_SYNC)
vty_out(vty, " OSPF ldp-sync debugging is on\n");
- /* Show debug status for GR helper. */
- if (IS_DEBUG_OSPF(gr, GR_HELPER) == OSPF_DEBUG_GR_HELPER)
- vty_out(vty, " OSPF Graceful Restart Helper debugging is on\n");
+ /* Show debug status for GR. */
+ if (IS_DEBUG_OSPF(gr, GR) == OSPF_DEBUG_GR)
+ vty_out(vty, " OSPF Graceful Restart debugging is on\n");
if (IS_DEBUG_OSPF(bfd, BFD_LIB) == OSPF_DEBUG_BFD_LIB)
vty_out(vty,
@@ -1953,9 +1948,9 @@ static int config_write_debug(struct vty *vty)
write = 1;
}
- /* debug ospf gr helper */
- if (IS_CONF_DEBUG_OSPF(gr, GR_HELPER) == OSPF_DEBUG_GR_HELPER) {
- vty_out(vty, "debug ospf%s graceful-restart helper\n", str);
+ /* debug ospf gr */
+ if (IS_CONF_DEBUG_OSPF(gr, GR) == OSPF_DEBUG_GR) {
+ vty_out(vty, "debug ospf%s graceful-restart\n", str);
write = 1;
}
diff --git a/ospfd/ospf_dump.h b/ospfd/ospf_dump.h
index b1c1d02a51..a1f55dd0af 100644
--- a/ospfd/ospf_dump.h
+++ b/ospfd/ospf_dump.h
@@ -64,8 +64,7 @@
#define OSPF_DEBUG_DEFAULTINFO 0x20
#define OSPF_DEBUG_LDP_SYNC 0x40
-#define OSPF_DEBUG_GR_HELPER 0x01
-#define OSPF_DEBUG_GR 0x03
+#define OSPF_DEBUG_GR 0x01
#define OSPF_DEBUG_BFD_LIB 0x01
@@ -118,7 +117,7 @@
#define IS_DEBUG_OSPF_DEFAULT_INFO IS_DEBUG_OSPF(defaultinfo, DEFAULTINFO)
#define IS_DEBUG_OSPF_LDP_SYNC IS_DEBUG_OSPF(ldp_sync, LDP_SYNC)
-#define IS_DEBUG_OSPF_GR_HELPER IS_DEBUG_OSPF(gr, GR_HELPER)
+#define IS_DEBUG_OSPF_GR IS_DEBUG_OSPF(gr, GR)
#define IS_CONF_DEBUG_OSPF_PACKET(a, b) \
(conf_debug_ospf_packet[a] & OSPF_DEBUG_##b)
diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c
index 6c1ac6761a..7fddb65a86 100644
--- a/ospfd/ospf_flood.c
+++ b/ospfd/ospf_flood.c
@@ -98,14 +98,14 @@ struct external_info *ospf_external_info_check(struct ospf *ospf,
int redist_on = 0;
redist_on =
- is_prefix_default(&p)
+ is_default_prefix4(&p)
? vrf_bitmap_check(
- zclient->default_information[AFI_IP],
- ospf->vrf_id)
+ zclient->default_information[AFI_IP],
+ ospf->vrf_id)
: (zclient->mi_redist[AFI_IP][type].enabled
|| vrf_bitmap_check(
- zclient->redist[AFI_IP][type],
- ospf->vrf_id));
+ zclient->redist[AFI_IP][type],
+ ospf->vrf_id));
// Pending: check for MI above.
if (redist_on) {
ext_list = ospf->external[type];
@@ -128,7 +128,7 @@ struct external_info *ospf_external_info_check(struct ospf *ospf,
}
}
- if (is_prefix_default(&p) && ospf->external[DEFAULT_ROUTE]) {
+ if (is_default_prefix4(&p) && ospf->external[DEFAULT_ROUTE]) {
ext_list = ospf->external[DEFAULT_ROUTE];
for (ALL_LIST_ELEMENTS_RO(ext_list, node, ext)) {
@@ -379,13 +379,13 @@ int ospf_flood(struct ospf *ospf, struct ospf_neighbor *nbr,
SET_FLAG(new->flags, OSPF_LSA_RECEIVED);
(void)ospf_lsa_is_self_originated(ospf, new); /* Let it set the flag */
- /* Received Grace LSA */
- if (IS_GRACE_LSA(new)) {
+ /* Received non-self-originated Grace LSA */
+ if (IS_GRACE_LSA(new) && !IS_LSA_SELF(new)) {
if (IS_LSA_MAXAGE(new)) {
/* Handling Max age grace LSA.*/
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Received a maxage GRACE-LSA from router %pI4",
__func__, &new->data->adv_router);
@@ -393,21 +393,21 @@ int ospf_flood(struct ospf *ospf, struct ospf_neighbor *nbr,
if (current) {
ospf_process_maxage_grace_lsa(ospf, new, nbr);
} else {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Grace LSA doesn't exist in lsdb, so discarding grace lsa",
__func__);
return -1;
}
} else {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Received a GRACE-LSA from router %pI4",
__func__, &new->data->adv_router);
if (ospf_process_grace_lsa(ospf, new, nbr)
== OSPF_GR_NOT_HELPER) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Not moving to HELPER role, So discarding grace LSA",
__func__);
@@ -445,9 +445,9 @@ int ospf_flood(struct ospf *ospf, struct ospf_neighbor *nbr,
}
/* OSPF LSA flooding -- RFC2328 Section 13.3. */
-static int ospf_flood_through_interface(struct ospf_interface *oi,
- struct ospf_neighbor *inbr,
- struct ospf_lsa *lsa)
+int ospf_flood_through_interface(struct ospf_interface *oi,
+ struct ospf_neighbor *inbr,
+ struct ospf_lsa *lsa)
{
struct ospf_neighbor *onbr;
struct route_node *rn;
@@ -1031,25 +1031,50 @@ void ospf_ls_retransmit_delete_nbr_as(struct ospf *ospf, struct ospf_lsa *lsa)
flushing an LSA from the whole domain. */
void ospf_lsa_flush_area(struct ospf_lsa *lsa, struct ospf_area *area)
{
+ struct ospf *ospf = area->ospf;
+
+ if (ospf_lsa_is_self_originated(ospf, lsa)
+ && ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+ zlog_debug(
+ "%s:LSA[Type%d:%pI4]: Graceful Restart in progress -- not flushing self-originated LSA",
+ ospf_get_name(ospf), lsa->data->type,
+ &lsa->data->id);
+ return;
+ }
+
/* Reset the lsa origination time such that it gives
more time for the ACK to be received and avoid
retransmissions */
lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("%s: MAXAGE set to LSA %pI4", __func__,
- &lsa->data->id);
+ zlog_debug("%s: MaxAge set to LSA[%s]", __func__,
+ dump_lsa_key(lsa));
monotime(&lsa->tv_recv);
lsa->tv_orig = lsa->tv_recv;
ospf_flood_through_area(area, NULL, lsa);
- ospf_lsa_maxage(area->ospf, lsa);
+ ospf_lsa_maxage(ospf, lsa);
}
void ospf_lsa_flush_as(struct ospf *ospf, struct ospf_lsa *lsa)
{
+ if (ospf_lsa_is_self_originated(ospf, lsa)
+ && ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+ zlog_debug(
+ "%s:LSA[Type%d:%pI4]: Graceful Restart in progress -- not flushing self-originated LSA",
+ ospf_get_name(ospf), lsa->data->type,
+ &lsa->data->id);
+ return;
+ }
+
/* Reset the lsa origination time such that it gives
more time for the ACK to be received and avoid
retransmissions */
lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_debug("%s: MaxAge set to LSA[%s]", __func__,
+ dump_lsa_key(lsa));
monotime(&lsa->tv_recv);
lsa->tv_orig = lsa->tv_recv;
ospf_flood_through_as(ospf, NULL, lsa);
diff --git a/ospfd/ospf_flood.h b/ospfd/ospf_flood.h
index 6f7ecfccf0..95a5b358c9 100644
--- a/ospfd/ospf_flood.h
+++ b/ospfd/ospf_flood.h
@@ -30,6 +30,9 @@ extern int ospf_flood_through_area(struct ospf_area *, struct ospf_neighbor *,
struct ospf_lsa *);
extern int ospf_flood_through_as(struct ospf *, struct ospf_neighbor *,
struct ospf_lsa *);
+extern int ospf_flood_through_interface(struct ospf_interface *oi,
+ struct ospf_neighbor *inbr,
+ struct ospf_lsa *lsa);
extern unsigned long ospf_ls_request_count(struct ospf_neighbor *);
extern int ospf_ls_request_isempty(struct ospf_neighbor *);
diff --git a/ospfd/ospf_gr.c b/ospfd/ospf_gr.c
new file mode 100644
index 0000000000..c108040303
--- /dev/null
+++ b/ospfd/ospf_gr.c
@@ -0,0 +1,824 @@
+/*
+ * This is an implementation of RFC 3623 Graceful OSPF Restart.
+ *
+ * Copyright 2021 NetDEF (c), All rights reserved.
+ * Copyright 2020 6WIND (c), All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "command.h"
+#include "table.h"
+#include "vty.h"
+#include "log.h"
+#include "printfrr.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_abr.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_opaque.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_gr.h"
+#include "ospfd/ospf_errors.h"
+#include "ospfd/ospf_dump.h"
+#ifndef VTYSH_EXTRACT_PL
+#include "ospfd/ospf_gr_clippy.c"
+#endif
+
+static void ospf_gr_nvm_delete(struct ospf *ospf);
+
+/* Lookup self-originated Grace-LSA in the LSDB. */
+static struct ospf_lsa *ospf_gr_lsa_lookup(struct ospf *ospf,
+ struct ospf_area *area)
+{
+ struct ospf_lsa *lsa;
+ struct in_addr lsa_id;
+ uint32_t lsa_id_host_byte_order;
+
+ lsa_id_host_byte_order = SET_OPAQUE_LSID(OPAQUE_TYPE_GRACE_LSA, 0);
+ lsa_id.s_addr = htonl(lsa_id_host_byte_order);
+ lsa = ospf_lsa_lookup(ospf, area, OSPF_OPAQUE_LINK_LSA, lsa_id,
+ ospf->router_id);
+
+ return lsa;
+}
+
+/* Fill in fields of the Grace-LSA that is being originated. */
+static void ospf_gr_lsa_body_set(struct ospf_gr_info *gr_info,
+ struct ospf_interface *oi, struct stream *s)
+{
+ struct grace_tlv_graceperiod tlv_period = {};
+ struct grace_tlv_restart_reason tlv_reason = {};
+ struct grace_tlv_restart_addr tlv_address = {};
+
+ /* Put grace period. */
+ tlv_period.header.type = htons(GRACE_PERIOD_TYPE);
+ tlv_period.header.length = htons(GRACE_PERIOD_LENGTH);
+ tlv_period.interval = htonl(gr_info->grace_period);
+ stream_put(s, &tlv_period, sizeof(tlv_period));
+
+ /* Put restart reason. */
+ tlv_reason.header.type = htons(RESTART_REASON_TYPE);
+ tlv_reason.header.length = htons(RESTART_REASON_LENGTH);
+ if (gr_info->restart_support)
+ tlv_reason.reason = OSPF_GR_SW_RESTART;
+ else
+ tlv_reason.reason = OSPF_GR_UNKNOWN_RESTART;
+ stream_put(s, &tlv_reason, sizeof(tlv_reason));
+
+ /* Put IP address. */
+ if (oi->type == OSPF_IFTYPE_BROADCAST || oi->type == OSPF_IFTYPE_NBMA
+ || oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) {
+ tlv_address.header.type = htons(RESTARTER_IP_ADDR_TYPE);
+ tlv_address.header.length = htons(RESTARTER_IP_ADDR_LEN);
+ tlv_address.addr = oi->address->u.prefix4;
+ stream_put(s, &tlv_address, sizeof(tlv_address));
+ }
+}
+
+/* Generate Grace-LSA for a given interface. */
+static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi)
+{
+ struct stream *s;
+ struct lsa_header *lsah;
+ struct ospf_lsa *new;
+ uint8_t options, lsa_type;
+ struct in_addr lsa_id;
+ uint32_t lsa_id_host_byte_order;
+ uint16_t length;
+
+ /* Create a stream for LSA. */
+ s = stream_new(OSPF_MAX_LSA_SIZE);
+
+ lsah = (struct lsa_header *)STREAM_DATA(s);
+
+ options = LSA_OPTIONS_GET(oi->area);
+ options |= LSA_OPTIONS_NSSA_GET(oi->area);
+ options |= OSPF_OPTION_O;
+
+ lsa_type = OSPF_OPAQUE_LINK_LSA;
+ lsa_id_host_byte_order = SET_OPAQUE_LSID(OPAQUE_TYPE_GRACE_LSA, 0);
+ lsa_id.s_addr = htonl(lsa_id_host_byte_order);
+
+ /* Set opaque-LSA header fields. */
+ lsa_header_set(s, options, lsa_type, lsa_id, oi->ospf->router_id);
+
+ /* Set opaque-LSA body fields. */
+ ospf_gr_lsa_body_set(&oi->ospf->gr_info, oi, s);
+
+ /* Set length. */
+ length = stream_get_endp(s);
+ lsah->length = htons(length);
+
+ /* Now, create an OSPF LSA instance. */
+ new = ospf_lsa_new_and_data(length);
+
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug("LSA[Type%d:%pI4]: Create an Opaque-LSA/GR instance",
+ lsa_type, &lsa_id);
+
+ new->area = oi->area;
+ new->oi = oi;
+ SET_FLAG(new->flags, OSPF_LSA_SELF);
+ memcpy(new->data, lsah, length);
+ stream_free(s);
+
+ return new;
+}
+
+/* Originate and install Grace-LSA for a given interface. */
+static void ospf_gr_lsa_originate(struct ospf_interface *oi)
+{
+ struct ospf_lsa *lsa, *old;
+
+ if (ospf_interface_neighbor_count(oi) == 0)
+ return;
+
+ /* Create new Grace-LSA. */
+ lsa = ospf_gr_lsa_new(oi);
+ if (!lsa) {
+ zlog_warn("%s: ospf_gr_lsa_new() failed", __func__);
+ return;
+ }
+
+ /* Find the old LSA and increase the seqno. */
+ old = ospf_gr_lsa_lookup(oi->ospf, oi->area);
+ if (old)
+ lsa->data->ls_seqnum = lsa_seqnum_increment(old);
+
+ /* Install this LSA into LSDB. */
+ if (ospf_lsa_install(oi->ospf, oi, lsa) == NULL) {
+ zlog_warn("%s: ospf_lsa_install() failed", __func__);
+ ospf_lsa_unlock(&lsa);
+ return;
+ }
+
+ /* Update new LSA origination count. */
+ oi->ospf->lsa_originate_count++;
+
+ /* Flood the LSA through out the interface */
+ ospf_flood_through_interface(oi, NULL, lsa);
+}
+
+/* Flush a given self-originated Grace-LSA. */
+static struct ospf_lsa *ospf_gr_flush_grace_lsa(struct ospf_interface *oi,
+ struct ospf_lsa *old)
+{
+ struct ospf_lsa *lsa;
+
+ if (ospf_interface_neighbor_count(oi) == 0)
+ return NULL;
+
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "GR: flushing self-originated Grace-LSAs [interface %s]",
+ oi->ifp->name);
+
+ lsa = ospf_lsa_dup(old);
+ lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+ lsa->data->ls_seqnum = lsa_seqnum_increment(lsa);
+
+ /* Install updated LSA into LSDB. */
+ if (ospf_lsa_install(oi->ospf, oi, lsa) == NULL) {
+ zlog_warn("%s: ospf_lsa_install() failed", __func__);
+ ospf_lsa_unlock(&lsa);
+ return NULL;
+ }
+
+ /* Flood the LSA through out the interface */
+ ospf_flood_through_interface(oi, NULL, lsa);
+
+ return lsa;
+}
+
+/* Flush all self-originated Grace-LSAs. */
+static void ospf_gr_flush_grace_lsas(struct ospf *ospf)
+{
+ struct ospf_area *area;
+ struct listnode *anode;
+
+ for (ALL_LIST_ELEMENTS_RO(ospf->areas, anode, area)) {
+ struct ospf_lsa *lsa;
+ struct ospf_interface *oi;
+ struct listnode *inode;
+
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "GR: flushing self-originated Grace-LSAs [area %pI4]",
+ &area->area_id);
+
+ lsa = ospf_gr_lsa_lookup(ospf, area);
+ if (!lsa) {
+ zlog_warn("%s: Grace-LSA not found [area %pI4]",
+ __func__, &area->area_id);
+ continue;
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi))
+ ospf_gr_flush_grace_lsa(oi, lsa);
+ }
+}
+
+/* Exit from the Graceful Restart mode. */
+static void ospf_gr_restart_exit(struct ospf *ospf, const char *reason)
+{
+ struct ospf_area *area;
+ struct listnode *onode, *anode;
+
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug("GR: exiting graceful restart: %s", reason);
+
+ ospf->gr_info.restart_in_progress = false;
+ OSPF_TIMER_OFF(ospf->gr_info.t_grace_period);
+
+ /* Record in non-volatile memory that the restart is complete. */
+ ospf_gr_nvm_delete(ospf);
+
+ for (ALL_LIST_ELEMENTS_RO(ospf->areas, onode, area)) {
+ struct ospf_interface *oi;
+
+ /*
+ * 1) The router should reoriginate its router-LSAs for all
+ * attached areas in order to make sure they have the correct
+ * contents.
+ */
+ ospf_router_lsa_update_area(area);
+
+ /*
+ * 2) The router should reoriginate network-LSAs on all segments
+ * where it is the Designated Router.
+ */
+ for (ALL_LIST_ELEMENTS_RO(area->oiflist, anode, oi))
+ if (oi->state == ISM_DR)
+ ospf_network_lsa_update(oi);
+ }
+
+ /*
+ * 5) Any received self-originated LSAs that are no longer valid should
+ * be flushed.
+ */
+ ospf_schedule_abr_task(ospf);
+
+ /*
+ * 3) The router reruns its OSPF routing calculations, this time
+ * installing the results into the system forwarding table, and
+ * originating summary-LSAs, Type-7 LSAs and AS-external-LSAs as
+ * necessary.
+ *
+ * 4) Any remnant entries in the system forwarding table that were
+ * installed before the restart, but that are no longer valid,
+ * should be removed.
+ */
+ ospf->gr_info.finishing_restart = true;
+ ospf_spf_calculate_schedule(ospf, SPF_FLAG_GR_FINISH);
+
+ /* 6) Any grace-LSAs that the router originated should be flushed. */
+ ospf_gr_flush_grace_lsas(ospf);
+}
+
+/* Check if a Router-LSA contains a given link. */
+static bool ospf_router_lsa_contains_adj(struct ospf_lsa *lsa,
+ struct in_addr *id)
+{
+ struct router_lsa *rl;
+
+ rl = (struct router_lsa *)lsa->data;
+ for (int i = 0; i < ntohs(rl->links); i++) {
+ struct in_addr *link_id = &rl->link[i].link_id;
+
+ if (rl->link[i].type != LSA_LINK_TYPE_POINTOPOINT)
+ continue;
+
+ if (IPV4_ADDR_SAME(id, link_id))
+ return true;
+ }
+
+ return false;
+}
+
+static bool ospf_gr_check_router_lsa_consistency(struct ospf *ospf,
+ struct ospf_area *area,
+ struct ospf_lsa *lsa)
+{
+ if (CHECK_FLAG(lsa->flags, OSPF_LSA_SELF)) {
+ struct ospf_lsa *lsa_self = lsa;
+ struct router_lsa *rl = (struct router_lsa *)lsa->data;
+
+ for (int i = 0; i < ntohs(rl->links); i++) {
+ struct in_addr *link_id = &rl->link[i].link_id;
+ struct ospf_lsa *lsa_adj;
+
+ if (rl->link[i].type != LSA_LINK_TYPE_POINTOPOINT)
+ continue;
+
+ lsa_adj = ospf_lsa_lookup_by_id(area, OSPF_ROUTER_LSA,
+ *link_id);
+ if (!lsa_adj)
+ continue;
+
+ if (!ospf_router_lsa_contains_adj(lsa_adj,
+ &lsa_self->data->id))
+ return false;
+ }
+ } else {
+ struct ospf_lsa *lsa_self;
+
+ lsa_self = ospf_lsa_lookup_by_id(area, OSPF_ROUTER_LSA,
+ ospf->router_id);
+ if (!lsa_self
+ || !CHECK_FLAG(lsa_self->flags, OSPF_LSA_RECEIVED))
+ return true;
+
+ if (ospf_router_lsa_contains_adj(lsa, &ospf->router_id)
+ != ospf_router_lsa_contains_adj(lsa_self, &lsa->data->id))
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Check for LSAs that are inconsistent with the pre-restart LSAs, and abort the
+ * ongoing graceful restart when that's the case.
+ */
+void ospf_gr_check_lsdb_consistency(struct ospf *ospf, struct ospf_area *area)
+{
+ struct route_node *rn;
+ struct ospf_lsa *lsa;
+
+ for (rn = route_top(ROUTER_LSDB(area)); rn; rn = route_next(rn)) {
+ lsa = rn->info;
+ if (!lsa)
+ continue;
+
+ if (!ospf_gr_check_router_lsa_consistency(ospf, area, lsa)) {
+ char reason[256];
+
+ snprintfrr(reason, sizeof(reason),
+ "detected inconsistent LSA[%s] [area %pI4]",
+ dump_lsa_key(lsa), &area->area_id);
+ ospf_gr_restart_exit(ospf, reason);
+ route_unlock_node(rn);
+ return;
+ }
+ }
+}
+
+/* Lookup neighbor by address in a given OSPF area. */
+static struct ospf_neighbor *
+ospf_area_nbr_lookup_by_addr(struct ospf_area *area, struct in_addr *addr)
+{
+ struct ospf_interface *oi;
+ struct ospf_neighbor *nbr;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(area->oiflist, node, oi)) {
+ nbr = ospf_nbr_lookup_by_addr(oi->nbrs, addr);
+ if (nbr)
+ return nbr;
+ }
+
+ return NULL;
+}
+
+/* Lookup neighbor by Router ID in a given OSPF area. */
+static struct ospf_neighbor *
+ospf_area_nbr_lookup_by_routerid(struct ospf_area *area, struct in_addr *id)
+{
+ struct ospf_interface *oi;
+ struct ospf_neighbor *nbr;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(area->oiflist, node, oi)) {
+ nbr = ospf_nbr_lookup_by_routerid(oi->nbrs, id);
+ if (nbr)
+ return nbr;
+ }
+
+ return NULL;
+}
+
+/* Check if there's a fully formed adjacency with the given neighbor ID. */
+static bool ospf_gr_check_adj_id(struct ospf_area *area,
+ struct in_addr *nbr_id)
+{
+ struct ospf_neighbor *nbr;
+
+ nbr = ospf_area_nbr_lookup_by_routerid(area, nbr_id);
+ if (!nbr || nbr->state < NSM_Full) {
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug("GR: missing adjacency to router %pI4",
+ nbr_id);
+ return false;
+ }
+
+ return true;
+}
+
+static bool ospf_gr_check_adjs_lsa_transit(struct ospf_area *area,
+ struct in_addr *link_id)
+{
+ struct ospf *ospf = area->ospf;
+ struct ospf_interface *oi;
+
+ /*
+ * Check if the transit network refers to a local interface (in which
+ * case it must be a DR for that network).
+ */
+ oi = ospf_if_lookup_by_local_addr(ospf, NULL, *link_id);
+ if (oi) {
+ struct ospf_lsa *lsa;
+ struct network_lsa *nlsa;
+ size_t cnt;
+
+ /* Lookup Network LSA corresponding to this interface. */
+ lsa = ospf_lsa_lookup_by_id(area, OSPF_NETWORK_LSA, *link_id);
+ if (!lsa)
+ return false;
+
+ /* Iterate over all routers present in the network. */
+ nlsa = (struct network_lsa *)lsa->data;
+ cnt = (lsa->size - (OSPF_LSA_HEADER_SIZE + 4)) / 4;
+ for (size_t i = 0; i < cnt; i++) {
+ struct in_addr *nbr_id = &nlsa->routers[i];
+
+ /* Skip self in the pseudonode. */
+ if (IPV4_ADDR_SAME(nbr_id, &ospf->router_id))
+ continue;
+
+ /*
+ * Check if there's a fully formed adjacency with this
+ * router.
+ */
+ if (!ospf_gr_check_adj_id(area, nbr_id))
+ return false;
+ }
+ } else {
+ struct ospf_neighbor *nbr;
+
+ /* Check if there's a fully formed adjacency with the DR. */
+ nbr = ospf_area_nbr_lookup_by_addr(area, link_id);
+ if (!nbr || nbr->state < NSM_Full) {
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "GR: missing adjacency to DR router %pI4",
+ link_id);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool ospf_gr_check_adjs_lsa(struct ospf_area *area, struct ospf_lsa *lsa)
+{
+ struct router_lsa *rl = (struct router_lsa *)lsa->data;
+
+ for (int i = 0; i < ntohs(rl->links); i++) {
+ struct in_addr *link_id = &rl->link[i].link_id;
+
+ switch (rl->link[i].type) {
+ case LSA_LINK_TYPE_POINTOPOINT:
+ if (!ospf_gr_check_adj_id(area, link_id))
+ return false;
+ break;
+ case LSA_LINK_TYPE_TRANSIT:
+ if (!ospf_gr_check_adjs_lsa_transit(area, link_id))
+ return false;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * Check if all adjacencies prior to the restart were reestablished.
+ *
+ * This is done using pre-restart Router LSAs and pre-restart Network LSAs
+ * received from the helping neighbors.
+ */
+void ospf_gr_check_adjs(struct ospf *ospf)
+{
+ struct ospf_area *area;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) {
+ struct ospf_lsa *lsa_self;
+
+ lsa_self = ospf_lsa_lookup_by_id(area, OSPF_ROUTER_LSA,
+ ospf->router_id);
+ if (!lsa_self || !ospf_gr_check_adjs_lsa(area, lsa_self)) {
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "GR: not all adjacencies were reestablished yet [area %pI4]",
+ &area->area_id);
+ return;
+ }
+ }
+
+ ospf_gr_restart_exit(ospf, "all adjacencies were reestablished");
+}
+
+/* Handling of grace period expiry. */
+static int ospf_gr_grace_period_expired(struct thread *thread)
+{
+ struct ospf *ospf = THREAD_ARG(thread);
+
+ ospf->gr_info.t_grace_period = NULL;
+ ospf_gr_restart_exit(ospf, "grace period has expired");
+
+ return 0;
+}
+
+/*
+ * Returns the path of the file (non-volatile memory) that contains GR status
+ * information.
+ */
+static char *ospf_gr_nvm_filepath(struct ospf *ospf)
+{
+ static char filepath[MAXPATHLEN];
+ char instance[16] = "";
+
+ if (ospf->instance)
+ snprintf(instance, sizeof(instance), "-%d", ospf->instance);
+ snprintf(filepath, sizeof(filepath), OSPFD_GR_STATE, instance);
+ return filepath;
+}
+
+/*
+ * Record in non-volatile memory that the given OSPF instance is attempting to
+ * perform a graceful restart.
+ */
+static void ospf_gr_nvm_update(struct ospf *ospf)
+{
+ char *filepath;
+ const char *inst_name;
+ json_object *json;
+ json_object *json_instances;
+ json_object *json_instance;
+
+ filepath = ospf_gr_nvm_filepath(ospf);
+ inst_name = ospf->name ? ospf->name : VRF_DEFAULT_NAME;
+
+ json = json_object_from_file(filepath);
+ if (json == NULL)
+ json = json_object_new_object();
+
+ json_object_object_get_ex(json, "instances", &json_instances);
+ if (!json_instances) {
+ json_instances = json_object_new_object();
+ json_object_object_add(json, "instances", json_instances);
+ }
+
+ json_object_object_get_ex(json_instances, inst_name, &json_instance);
+ if (!json_instance) {
+ json_instance = json_object_new_object();
+ json_object_object_add(json_instances, inst_name,
+ json_instance);
+ }
+
+ /*
+ * Record not only the grace period, but also a UNIX timestamp
+ * corresponding to the end of that period. That way, once ospfd is
+ * restarted, it will be possible to take into account the time that
+ * passed while ospfd wasn't running.
+ */
+ json_object_int_add(json_instance, "gracePeriod",
+ ospf->gr_info.grace_period);
+ json_object_int_add(json_instance, "timestamp",
+ time(NULL) + ospf->gr_info.grace_period);
+
+ json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY);
+ json_object_free(json);
+}
+
+/*
+ * Delete GR status information about the given OSPF instance from non-volatile
+ * memory.
+ */
+static void ospf_gr_nvm_delete(struct ospf *ospf)
+{
+ char *filepath;
+ const char *inst_name;
+ json_object *json;
+ json_object *json_instances;
+
+ filepath = ospf_gr_nvm_filepath(ospf);
+ inst_name = ospf->name ? ospf->name : VRF_DEFAULT_NAME;
+
+ json = json_object_from_file(filepath);
+ if (json == NULL)
+ json = json_object_new_object();
+
+ json_object_object_get_ex(json, "instances", &json_instances);
+ if (!json_instances) {
+ json_instances = json_object_new_object();
+ json_object_object_add(json, "instances", json_instances);
+ }
+
+ json_object_object_del(json_instances, inst_name);
+
+ json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY);
+ json_object_free(json);
+}
+
+/*
+ * Fetch from non-volatile memory whether the given OSPF instance is performing
+ * a graceful shutdown or not.
+ */
+void ospf_gr_nvm_read(struct ospf *ospf)
+{
+ char *filepath;
+ const char *inst_name;
+ json_object *json;
+ json_object *json_instances;
+ json_object *json_instance;
+ json_object *json_timestamp;
+ time_t timestamp = 0;
+
+ filepath = ospf_gr_nvm_filepath(ospf);
+ inst_name = ospf->name ? ospf->name : VRF_DEFAULT_NAME;
+
+ json = json_object_from_file(filepath);
+ if (json == NULL)
+ json = json_object_new_object();
+
+ json_object_object_get_ex(json, "instances", &json_instances);
+ if (!json_instances) {
+ json_instances = json_object_new_object();
+ json_object_object_add(json, "instances", json_instances);
+ }
+
+ json_object_object_get_ex(json_instances, inst_name, &json_instance);
+ if (!json_instance) {
+ json_instance = json_object_new_object();
+ json_object_object_add(json_instances, inst_name,
+ json_instance);
+ }
+
+ json_object_object_get_ex(json_instance, "timestamp", &json_timestamp);
+ if (json_timestamp) {
+ time_t now;
+ unsigned long remaining_time;
+
+ /* Check if the grace period has already expired. */
+ now = time(NULL);
+ timestamp = json_object_get_int(json_timestamp);
+ if (now > timestamp) {
+ ospf_gr_restart_exit(
+ ospf, "grace period has expired already");
+ } else {
+ /* Schedule grace period timeout. */
+ ospf->gr_info.restart_in_progress = true;
+ remaining_time = timestamp - time(NULL);
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "GR: remaining time until grace period expires: %lu(s)",
+ remaining_time);
+ thread_add_timer(master, ospf_gr_grace_period_expired,
+ ospf, remaining_time,
+ &ospf->gr_info.t_grace_period);
+ }
+ }
+
+ json_object_object_del(json_instances, inst_name);
+
+ json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY);
+ json_object_free(json);
+}
+
+/* Prepare to start a Graceful Restart. */
+static void ospf_gr_prepare(void)
+{
+ struct ospf *ospf;
+ struct ospf_interface *oi;
+ struct listnode *onode;
+
+ for (ALL_LIST_ELEMENTS_RO(om->ospf, onode, ospf)) {
+ struct listnode *inode;
+
+ if (!ospf->gr_info.restart_support
+ || ospf->gr_info.prepare_in_progress)
+ continue;
+
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "GR: preparing to perform a graceful restart [period %u second(s)] [vrf %s]",
+ ospf->gr_info.grace_period,
+ ospf_vrf_id_to_name(ospf->vrf_id));
+
+ if (!CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE)) {
+ zlog_warn(
+ "%s: failed to activate graceful restart: opaque capability not enabled",
+ __func__);
+ continue;
+ }
+
+ /* Freeze OSPF routes in the RIB. */
+ if (ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period)) {
+ zlog_warn(
+ "%s: failed to activate graceful restart: not connected to zebra",
+ __func__);
+ continue;
+ }
+
+ /* Send a Grace-LSA to all neighbors. */
+ for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, inode, oi))
+ ospf_gr_lsa_originate(oi);
+
+ /* Record end of the grace period in non-volatile memory. */
+ ospf_gr_nvm_update(ospf);
+
+ /*
+ * Mark that a Graceful Restart preparation is in progress, to
+ * prevent ospfd from flushing its self-originated LSAs on exit.
+ */
+ ospf->gr_info.prepare_in_progress = true;
+ }
+}
+
+DEFPY(graceful_restart_prepare, graceful_restart_prepare_cmd,
+ "graceful-restart prepare ip ospf",
+ "Graceful Restart commands\n"
+ "Prepare upcoming graceful restart\n"
+ IP_STR
+ "Prepare to restart the OSPF process")
+{
+ ospf_gr_prepare();
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(graceful_restart, graceful_restart_cmd,
+ "graceful-restart [grace-period (1-1800)$grace_period]",
+ OSPF_GR_STR
+ "Maximum length of the 'grace period'\n"
+ "Maximum length of the 'grace period' in seconds\n")
+{
+ VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+
+ /* Check and get restart period if present. */
+ if (!grace_period_str)
+ grace_period = OSPF_DFLT_GRACE_INTERVAL;
+
+ ospf->gr_info.restart_support = true;
+ ospf->gr_info.grace_period = grace_period;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(no_graceful_restart, no_graceful_restart_cmd,
+ "no graceful-restart [period (1-1800)]",
+ NO_STR OSPF_GR_STR
+ "Maximum length of the 'grace period'\n"
+ "Maximum length of the 'grace period' in seconds\n")
+{
+ VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+
+ if (!ospf->gr_info.restart_support)
+ return CMD_SUCCESS;
+
+ if (ospf->gr_info.prepare_in_progress) {
+ vty_out(vty,
+ "%% Error: Graceful Restart preparation in progress\n");
+ return CMD_WARNING;
+ }
+
+ ospf->gr_info.restart_support = false;
+ ospf->gr_info.grace_period = OSPF_DFLT_GRACE_INTERVAL;
+
+ return CMD_SUCCESS;
+}
+
+void ospf_gr_init(void)
+{
+ install_element(ENABLE_NODE, &graceful_restart_prepare_cmd);
+ install_element(OSPF_NODE, &graceful_restart_cmd);
+ install_element(OSPF_NODE, &no_graceful_restart_cmd);
+}
diff --git a/ospfd/ospf_gr_helper.h b/ospfd/ospf_gr.h
index bd6d1d7462..496ba3c597 100644
--- a/ospfd/ospf_gr_helper.h
+++ b/ospfd/ospf_gr.h
@@ -21,8 +21,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef _ZEBRA_OSPF_GR_HELPER_H
-#define _ZEBRA_OSPF_GR_HELPER_H
+#ifndef _ZEBRA_OSPF_GR_H
+#define _ZEBRA_OSPF_GR_H
#define OSPF_GR_NOT_HELPER 0
#define OSPF_GR_ACTIVE_HELPER 1
@@ -32,6 +32,7 @@
#define OSPF_MAX_GRACE_INTERVAL 1800
#define OSPF_MIN_GRACE_INTERVAL 1
+#define OSPF_DFLT_GRACE_INTERVAL 120
enum ospf_helper_exit_reason {
OSPF_GR_HELPER_EXIT_NONE = 0,
@@ -55,7 +56,8 @@ enum ospf_gr_helper_rejected_reason {
OSPF_HELPER_NOT_A_VALID_NEIGHBOUR,
OSPF_HELPER_PLANNED_ONLY_RESTART,
OSPF_HELPER_TOPO_CHANGE_RTXMT_LIST,
- OSPF_HELPER_LSA_AGE_MORE
+ OSPF_HELPER_LSA_AGE_MORE,
+ OSPF_HELPER_RESTARTING,
};
/* Ref RFC3623 appendex-A */
@@ -101,7 +103,8 @@ struct ospf_helper_info {
/* Grace timer,This Router acts as
* helper until this timer until
- * this timer expires*/
+ * this timer expires.
+ */
struct thread *t_grace_timer;
/* Helper status */
@@ -178,4 +181,11 @@ extern void ospf_gr_helper_supported_gracetime_set(struct ospf *ospf,
uint32_t interval);
extern void ospf_gr_helper_set_supported_planned_only_restart(struct ospf *ospf,
bool planned_only);
-#endif /* _ZEBRA_OSPF_HELPER_H */
+
+extern void ospf_gr_check_lsdb_consistency(struct ospf *ospf,
+ struct ospf_area *area);
+extern void ospf_gr_check_adjs(struct ospf *ospf);
+extern void ospf_gr_nvm_read(struct ospf *ospf);
+extern void ospf_gr_init(void);
+
+#endif /* _ZEBRA_OSPF_GR_H */
diff --git a/ospfd/ospf_gr_helper.c b/ospfd/ospf_gr_helper.c
index b32318b832..11ad45d30f 100644
--- a/ospfd/ospf_gr_helper.c
+++ b/ospfd/ospf_gr_helper.c
@@ -48,7 +48,7 @@
#include "ospfd/ospf_errors.h"
#include "ospfd/ospf_nsm.h"
#include "ospfd/ospf_ism.h"
-#include "ospfd/ospf_gr_helper.h"
+#include "ospfd/ospf_gr.h"
static const char * const ospf_exit_reason_desc[] = {
"Unknown reason",
@@ -72,6 +72,7 @@ static const char * const ospf_rejected_reason_desc[] = {
"Supports only planned restart but received unplanned",
"Topo change due to change in lsa rxmt list",
"LSA age is more than Grace interval",
+ "Router is in the process of graceful restart",
};
static void show_ospf_grace_lsa_info(struct vty *vty, struct ospf_lsa *lsa);
@@ -161,7 +162,7 @@ const char *ospf_rejected_reason2str(unsigned int reason)
*/
void ospf_gr_helper_instance_init(struct ospf *ospf)
{
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s, GR Helper init.", __func__);
ospf->is_helper_supported = OSPF_GR_FALSE;
@@ -187,7 +188,7 @@ void ospf_gr_helper_instance_init(struct ospf *ospf)
*/
void ospf_gr_helper_instance_stop(struct ospf *ospf)
{
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s, GR helper deinit.", __func__);
ospf_enable_rtr_hash_destroy(ospf);
@@ -203,7 +204,7 @@ void ospf_gr_helper_init(void)
{
int rc;
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s, GR Helper init.", __func__);
rc = ospf_register_opaque_functab(
@@ -225,8 +226,7 @@ void ospf_gr_helper_init(void)
*/
void ospf_gr_helper_stop(void)
{
-
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s, GR helper deinit.", __func__);
ospf_delete_opaque_functab(OSPF_OPAQUE_LINK_LSA, OPAQUE_TYPE_GRACE_LSA);
@@ -259,7 +259,7 @@ static int ospf_extract_grace_lsa_fields(struct ospf_lsa *lsa,
/* Check LSA len */
if (lsa->size <= OSPF_LSA_HEADER_SIZE) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s: Malformed packet: Invalid LSA len:%d",
__func__, length);
return OSPF_GR_FAILURE;
@@ -272,7 +272,7 @@ static int ospf_extract_grace_lsa_fields(struct ospf_lsa *lsa,
/* Check TLV len against overall LSA */
if (sum + TLV_SIZE(tlvh) > length) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s: Malformed packet: Invalid TLV len:%u",
__func__, TLV_SIZE(tlvh));
return OSPF_GR_FAILURE;
@@ -324,7 +324,7 @@ static int ospf_extract_grace_lsa_fields(struct ospf_lsa *lsa,
sum += TLV_SIZE(tlvh);
break;
default:
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Malformed packet.Invalid TLV type:%d",
__func__, ntohs(tlvh->type));
@@ -391,12 +391,12 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
ret = ospf_extract_grace_lsa_fields(lsa, &grace_interval, &restart_addr,
&restart_reason);
if (ret != OSPF_GR_SUCCESS) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s, Wrong Grace LSA packet.", __func__);
return OSPF_GR_NOT_HELPER;
}
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Grace LSA received from %pI4, grace interval:%u, restart reason:%s",
__func__, &restart_addr, grace_interval,
@@ -410,7 +410,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
restarter = ospf_nbr_lookup_by_addr(oi->nbrs, &restart_addr);
if (!restarter) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Restarter is not a nbr(%pI4) for this router.",
__func__, &restart_addr);
@@ -427,7 +427,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
lookup.advRtrAddr.s_addr = restarter->router_id.s_addr;
if (!hash_lookup(ospf->enable_rtr_list, &lookup)) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, HELPER support is disabled, So not a HELPER",
__func__);
@@ -442,7 +442,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
* became a adjacency.
*/
if (!IS_NBR_STATE_FULL(restarter)) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, This Neighbour %pI4 is not in FULL state.",
__func__, &restarter->src);
@@ -456,7 +456,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
*/
if (ospf->only_planned_restart
&& !OSPF_GR_IS_PLANNED_RESTART(restart_reason)) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Router supports only planned restarts but received the GRACE LSA for an unplanned restart.",
__func__);
@@ -470,7 +470,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
*/
if (ospf->strict_lsa_check && !ospf_ls_retransmit_isempty(restarter)
&& ospf_check_change_in_rxmt_list(restarter)) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Changed LSA in Rxmt list. So not Helper.",
__func__);
@@ -481,7 +481,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
/*LSA age must be less than the grace period */
if (ntohs(lsa->data->ls_age) >= grace_interval) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Grace LSA age(%d) is more than the grace interval(%d)",
__func__, lsa->data->ls_age, grace_interval);
@@ -490,6 +490,16 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
return OSPF_GR_NOT_HELPER;
}
+ if (ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "%s: router is in the process of graceful restart",
+ __func__);
+ restarter->gr_helper_info.rejected_reason =
+ OSPF_HELPER_RESTARTING;
+ return OSPF_GR_NOT_HELPER;
+ }
+
/* check supported grace period configured
* if configured, use this to start the grace
* timer otherwise use the interval received
@@ -497,7 +507,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
*/
actual_grace_interval = grace_interval;
if (grace_interval > ospf->supported_grace_time) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Received grace period %d is larger than supported grace %d",
__func__, grace_interval,
@@ -512,12 +522,12 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
if (ospf->active_restarter_cnt > 0)
ospf->active_restarter_cnt--;
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Router is already acting as a HELPER for this nbr,so restart the grace timer",
__func__);
} else {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, This Router becomes a HELPER for the neighbour %pI4",
__func__, &restarter->src);
@@ -535,7 +545,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
/* Increment the active restarter count */
ospf->active_restarter_cnt++;
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s, Grace timer started.interval:%d", __func__,
actual_grace_interval);
@@ -622,10 +632,9 @@ void ospf_helper_handle_topo_chg(struct ospf *ospf, struct ospf_lsa *lsa)
if (!ospf->strict_lsa_check)
return;
- if (IS_DEBUG_OSPF_GR_HELPER)
- zlog_debug(
- "%s, Topo change detected due to lsa LSID:%pI4 type:%d",
- __func__, &lsa->data->id, lsa->data->type);
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug("%s: Topo change detected due to LSA[%s]", __func__,
+ dump_lsa_key(lsa));
lsa->to_be_acknowledged = OSPF_GR_TRUE;
@@ -686,7 +695,7 @@ void ospf_gr_helper_exit(struct ospf_neighbor *nbr,
if (!OSPF_GR_IS_ACTIVE_HELPER(nbr))
return;
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s, Exiting from HELPER support to %pI4, due to %s",
__func__, &nbr->src, ospf_exit_reason2str(reason));
@@ -717,7 +726,7 @@ void ospf_gr_helper_exit(struct ospf_neighbor *nbr,
* If no, bring down the neighbour.
*/
if (reason != OSPF_GR_HELPER_COMPLETED) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Failed GR exit, so bringing down the neighbour",
__func__);
@@ -768,12 +777,12 @@ void ospf_process_maxage_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
ret = ospf_extract_grace_lsa_fields(lsa, &graceInterval, &restartAddr,
&restartReason);
if (ret != OSPF_GR_SUCCESS) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s, Wrong Grace LSA packet.", __func__);
return;
}
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s, GraceLSA received for neighbour %pI4", __func__,
&restartAddr);
@@ -785,7 +794,7 @@ void ospf_process_maxage_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
restarter = ospf_nbr_lookup_by_addr(oi->nbrs, &restartAddr);
if (!restarter) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Restarter is not a neighbour for this router.",
__func__);
diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c
index c850df55bb..9ef2a6520a 100644
--- a/ospfd/ospf_lsa.c
+++ b/ospfd/ospf_lsa.c
@@ -819,6 +819,14 @@ static struct ospf_lsa *ospf_router_lsa_originate(struct ospf_area *area)
{
struct ospf_lsa *new;
+ if (area->ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+ zlog_debug(
+ "LSA[Type%d]: Graceful Restart in progress, don't originate",
+ OSPF_ROUTER_LSA);
+ return NULL;
+ }
+
/* Create new router-LSA instance. */
if ((new = ospf_router_lsa_new(area)) == NULL) {
zlog_err("%s: ospf_router_lsa_new returned NULL", __func__);
@@ -1045,6 +1053,14 @@ void ospf_network_lsa_update(struct ospf_interface *oi)
{
struct ospf_lsa *new;
+ if (oi->area->ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+ zlog_debug(
+ "LSA[Type%d]: Graceful Restart in progress, don't originate",
+ OSPF_NETWORK_LSA);
+ return;
+ }
+
if (oi->network_lsa_self != NULL) {
ospf_lsa_refresh(oi->ospf, oi->network_lsa_self);
return;
@@ -1212,6 +1228,14 @@ struct ospf_lsa *ospf_summary_lsa_originate(struct prefix_ipv4 *p,
struct ospf_lsa *new;
struct in_addr id;
+ if (area->ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+ zlog_debug(
+ "LSA[Type%d]: Graceful Restart in progress, don't originate",
+ OSPF_SUMMARY_LSA);
+ return NULL;
+ }
+
id = ospf_lsa_unique_id(area->ospf, area->lsdb, OSPF_SUMMARY_LSA, p);
if (id.s_addr == 0xffffffff) {
@@ -1353,6 +1377,14 @@ struct ospf_lsa *ospf_summary_asbr_lsa_originate(struct prefix_ipv4 *p,
struct ospf_lsa *new;
struct in_addr id;
+ if (area->ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+ zlog_debug(
+ "LSA[Type%d]: Graceful Restart in progress, don't originate",
+ OSPF_ASBR_SUMMARY_LSA);
+ return NULL;
+ }
+
id = ospf_lsa_unique_id(area->ospf, area->lsdb, OSPF_ASBR_SUMMARY_LSA,
p);
@@ -1551,8 +1583,8 @@ static void ospf_external_lsa_body_set(struct stream *s,
stream_put_ipv4(s, mask.s_addr);
/* If prefix is default, specify DEFAULT_ROUTE. */
- type = is_prefix_default(&ei->p) ? DEFAULT_ROUTE : ei->type;
- instance = is_prefix_default(&ei->p) ? 0 : ei->instance;
+ type = is_default_prefix4(&ei->p) ? DEFAULT_ROUTE : ei->type;
+ instance = is_default_prefix4(&ei->p) ? 0 : ei->instance;
mtype = (ROUTEMAP_METRIC_TYPE(ei) != -1)
? ROUTEMAP_METRIC_TYPE(ei)
@@ -1799,6 +1831,13 @@ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf,
struct ospf_lsa *new;
struct as_external_lsa *extnew;
+ if (ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+ zlog_debug(
+ "LSA[Translated Type5]: Graceful Restart in progress, don't originate");
+ return NULL;
+ }
+
/* we cant use ospf_external_lsa_originate() as we need to set
* the OSPF_LSA_LOCAL_XLT flag, must originate by hand
*/
@@ -1947,23 +1986,19 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf,
return new;
}
-int is_prefix_default(struct prefix_ipv4 *p)
-{
- struct prefix_ipv4 q;
-
- q.family = AF_INET;
- q.prefix.s_addr = INADDR_ANY;
- q.prefixlen = 0;
-
- return prefix_same((struct prefix *)p, (struct prefix *)&q);
-}
-
/* Originate an AS-external-LSA, install and flood. */
struct ospf_lsa *ospf_external_lsa_originate(struct ospf *ospf,
struct external_info *ei)
{
struct ospf_lsa *new;
+ if (ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+ zlog_debug(
+ "LSA[Type5]: Graceful Restart in progress, don't originate");
+ return NULL;
+ }
+
/* Added for NSSA project....
External LSAs are originated in ASBRs as usual, but for NSSA
@@ -2113,8 +2148,7 @@ void ospf_external_lsa_rid_change(struct ospf *ospf)
if (!ei)
continue;
- if (is_prefix_default(
- (struct prefix_ipv4 *)&ei->p))
+ if (is_default_prefix4(&ei->p))
continue;
lsa = ospf_external_info_find_lsa(ospf, &ei->p);
@@ -2284,7 +2318,7 @@ void ospf_external_lsa_refresh_type(struct ospf *ospf, uint8_t type,
rn = route_next(rn)) {
ei = rn->info;
if (ei) {
- if (!is_prefix_default(&ei->p)) {
+ if (!is_default_prefix4(&ei->p)) {
struct ospf_lsa *lsa;
struct ospf_external_aggr_rt *aggr;
@@ -2791,8 +2825,8 @@ struct ospf_lsa *ospf_lsa_install(struct ospf *ospf, struct ospf_interface *oi,
*/
if (IS_LSA_MAXAGE(new)) {
if (IS_DEBUG_OSPF(lsa, LSA_INSTALL))
- zlog_debug("LSA[Type%d:%pI4]: Install LSA %p, MaxAge",
- new->data->type, &new->data->id, lsa);
+ zlog_debug("LSA[%s]: Install LSA %p, MaxAge",
+ dump_lsa_key(new), lsa);
ospf_lsa_maxage(ospf, lsa);
}
@@ -2874,9 +2908,8 @@ static int ospf_maxage_lsa_remover(struct thread *thread)
if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
zlog_debug(
- "LSA[Type%d:%pI4]: MaxAge LSA removed from list",
- lsa->data->type,
- &lsa->data->id);
+ "LSA[%s]: MaxAge LSA removed from list",
+ dump_lsa_key(lsa));
if (CHECK_FLAG(lsa->flags, OSPF_LSA_PREMATURE_AGE)) {
if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
@@ -2894,9 +2927,8 @@ static int ospf_maxage_lsa_remover(struct thread *thread)
*/
if (old != lsa) {
flog_err(EC_OSPF_LSA_MISSING,
- "%s: LSA[Type%d:%pI4]: LSA not in LSDB",
- __func__, lsa->data->type,
- &lsa->data->id);
+ "%s: LSA[%s]: LSA not in LSDB",
+ __func__, dump_lsa_key(lsa));
continue;
}
@@ -2905,9 +2937,8 @@ static int ospf_maxage_lsa_remover(struct thread *thread)
} else {
if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
zlog_debug(
- "%s: LSA[Type%d:%pI4]: No associated LSDB!",
- __func__, lsa->data->type,
- &lsa->data->id);
+ "%s: LSA[%s]: No associated LSDB!",
+ __func__, dump_lsa_key(lsa));
}
}
@@ -2964,9 +2995,8 @@ void ospf_lsa_maxage(struct ospf *ospf, struct ospf_lsa *lsa)
if (CHECK_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE)) {
if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
zlog_debug(
- "LSA[Type%d:%pI4]: %p already exists on MaxAge LSA list",
- lsa->data->type, &lsa->data->id,
- (void *)lsa);
+ "LSA[%s]: %p already exists on MaxAge LSA list",
+ dump_lsa_key(lsa), lsa);
return;
}
diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h
index 3808700ccc..d01dc720ba 100644
--- a/ospfd/ospf_lsa.h
+++ b/ospfd/ospf_lsa.h
@@ -331,7 +331,6 @@ extern void ospf_lsa_maxage_delete(struct ospf *, struct ospf_lsa *);
extern void ospf_discard_from_db(struct ospf *, struct ospf_lsdb *,
struct ospf_lsa *);
-extern int is_prefix_default(struct prefix_ipv4 *);
extern int metric_type(struct ospf *, uint8_t, unsigned short);
extern int metric_value(struct ospf *, uint8_t, unsigned short);
diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c
index d94de12994..73d596b030 100644
--- a/ospfd/ospf_main.c
+++ b/ospfd/ospf_main.c
@@ -54,6 +54,7 @@
#include "ospfd/ospf_zebra.h"
#include "ospfd/ospf_vty.h"
#include "ospfd/ospf_bfd.h"
+#include "ospfd/ospf_gr.h"
#include "ospfd/ospf_errors.h"
#include "ospfd/ospf_ldp_sync.h"
#include "ospfd/ospf_routemap_nb.h"
@@ -225,6 +226,7 @@ int main(int argc, char **argv)
ospf_route_map_init();
ospf_opaque_init();
+ ospf_gr_init();
ospf_gr_helper_init();
/* OSPF errors init */
diff --git a/ospfd/ospf_neighbor.c b/ospfd/ospf_neighbor.c
index 8725497f2d..98fb54d82a 100644
--- a/ospfd/ospf_neighbor.c
+++ b/ospfd/ospf_neighbor.c
@@ -44,7 +44,7 @@
#include "ospfd/ospf_flood.h"
#include "ospfd/ospf_dump.h"
#include "ospfd/ospf_bfd.h"
-#include "ospfd/ospf_gr_helper.h"
+#include "ospfd/ospf_gr.h"
/* Fill in the the 'key' as appropriate to retrieve the entry for nbr
* from the ospf_interface's nbrs table. Indexed by interface address
diff --git a/ospfd/ospf_neighbor.h b/ospfd/ospf_neighbor.h
index 2ce6d6755c..433b136153 100644
--- a/ospfd/ospf_neighbor.h
+++ b/ospfd/ospf_neighbor.h
@@ -22,7 +22,7 @@
#ifndef _ZEBRA_OSPF_NEIGHBOR_H
#define _ZEBRA_OSPF_NEIGHBOR_H
-#include <ospfd/ospf_gr_helper.h>
+#include <ospfd/ospf_gr.h>
#include <ospfd/ospf_packet.h>
/* Neighbor Data Structure */
diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c
index b3b9244b22..892d264a2d 100644
--- a/ospfd/ospf_nsm.c
+++ b/ospfd/ospf_nsm.c
@@ -49,6 +49,7 @@
#include "ospfd/ospf_flood.h"
#include "ospfd/ospf_abr.h"
#include "ospfd/ospf_bfd.h"
+#include "ospfd/ospf_gr.h"
#include "ospfd/ospf_errors.h"
DEFINE_HOOK(ospf_nsm_change,
@@ -75,7 +76,7 @@ static int ospf_inactivity_timer(struct thread *thread)
*/
if (!OSPF_GR_IS_ACTIVE_HELPER(nbr))
OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_InactivityTimer);
- else if (IS_DEBUG_OSPF_GR_HELPER)
+ else if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Acting as HELPER for this neighbour, So inactivitytimer event will not be fired.",
__func__);
@@ -727,6 +728,9 @@ static void nsm_change_state(struct ospf_neighbor *nbr, int state)
ospf_network_lsa_update(oi);
}
}
+
+ if (state == NSM_Full && oi->ospf->gr_info.restart_in_progress)
+ ospf_gr_check_adjs(oi->ospf);
}
ospf_opaque_nsm_change(nbr, old_state);
diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c
index fac2f97141..9ec2ed0aa8 100644
--- a/ospfd/ospf_opaque.c
+++ b/ospfd/ospf_opaque.c
@@ -2147,6 +2147,11 @@ void ospf_opaque_self_originated_lsa_received(struct ospf_neighbor *nbr,
if ((top = oi_to_top(nbr->oi)) == NULL)
return;
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_debug(
+ "LSA[Type%d:%pI4]: processing self-originated Opaque-LSA",
+ lsa->data->type, &lsa->data->id);
+
/*
* Since these LSA entries are not yet installed into corresponding
* LSDB, just flush them without calling ospf_ls_maxage() afterward.
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c
index 580eee13cf..9930b0bd49 100644
--- a/ospfd/ospf_packet.c
+++ b/ospfd/ospf_packet.c
@@ -54,7 +54,7 @@
#include "ospfd/ospf_dump.h"
#include "ospfd/ospf_errors.h"
#include "ospfd/ospf_zebra.h"
-#include "ospfd/ospf_gr_helper.h"
+#include "ospfd/ospf_gr.h"
/*
* OSPF Fragmentation / fragmented writes
@@ -2025,9 +2025,11 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph,
ospf_ls_ack_send(nbr, lsa);
- ospf_opaque_self_originated_lsa_received(nbr,
- lsa);
- continue;
+ if (!ospf->gr_info.restart_in_progress) {
+ ospf_opaque_self_originated_lsa_received(
+ nbr, lsa);
+ continue;
+ }
}
}
@@ -2213,6 +2215,9 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph,
assert(listcount(lsas) == 0);
list_delete(&lsas);
+
+ if (ospf->gr_info.restart_in_progress)
+ ospf_gr_check_lsdb_consistency(oi->ospf, oi->area);
}
/* OSPF Link State Acknowledgment message read -- RFC2328 Section 13.7. */
@@ -3575,14 +3580,13 @@ static int ospf_make_ls_upd(struct ospf_interface *oi, struct list *update,
struct lsa_header *lsah;
uint16_t ls_age;
- if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("ospf_make_ls_upd: List Iteration %d",
- count);
-
lsa = listgetdata(node);
-
assert(lsa->data);
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_debug("%s: List Iteration %d LSA[%s]", __func__,
+ count, dump_lsa_key(lsa));
+
/* Will it fit? Minimum it has to fit atleast one */
if ((length + delta + ntohs(lsa->data->length) > size_noauth) &&
(count > 0))
@@ -4264,7 +4268,7 @@ void ospf_ls_ack_send(struct ospf_neighbor *nbr, struct ospf_lsa *lsa)
struct ospf_interface *oi = nbr->oi;
if (IS_GRACE_LSA(lsa)) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s, Sending GRACE ACK to Restarter.",
__func__);
}
diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c
index 43d6ff44ba..6a51440266 100644
--- a/ospfd/ospf_spf.c
+++ b/ospfd/ospf_spf.c
@@ -1903,6 +1903,8 @@ static int ospf_spf_calculate_schedule_worker(struct thread *thread)
strlcat(rbuf, "ASBR, ", sizeof(rbuf));
if (spf_reason_flags & (1 << SPF_FLAG_MAXAGE))
strlcat(rbuf, "M, ", sizeof(rbuf));
+ if (spf_reason_flags & (1 << SPF_FLAG_GR_FINISH))
+ strlcat(rbuf, "GR, ", sizeof(rbuf));
size_t rbuflen = strlen(rbuf);
if (rbuflen >= 2)
diff --git a/ospfd/ospf_spf.h b/ospfd/ospf_spf.h
index 4ff4a6d125..20f38440aa 100644
--- a/ospfd/ospf_spf.h
+++ b/ospfd/ospf_spf.h
@@ -69,6 +69,7 @@ typedef enum {
SPF_FLAG_ABR_STATUS_CHANGE,
SPF_FLAG_ASBR_STATUS_CHANGE,
SPF_FLAG_CONFIG_CHANGE,
+ SPF_FLAG_GR_FINISH,
} ospf_spf_reason_t;
extern void ospf_spf_calculate_schedule(struct ospf *, ospf_spf_reason_t);
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index db8e961b8b..bf2a8564f0 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -10027,7 +10027,7 @@ DEFUN (ospf_external_route_aggregation,
str2prefix_ipv4(argv[idx]->arg, &p);
- if (is_prefix_default(&p)) {
+ if (is_default_prefix4(&p)) {
vty_out(vty,
"Default address shouldn't be configured as summary address.\n");
return CMD_SUCCESS;
@@ -10068,7 +10068,7 @@ DEFUN (no_ospf_external_route_aggregation,
str2prefix_ipv4(argv[idx]->arg, &p);
- if (is_prefix_default(&p)) {
+ if (is_default_prefix4(&p)) {
vty_out(vty,
"Default address shouldn't be configured as summary address.\n");
return CMD_SUCCESS;
@@ -10361,7 +10361,7 @@ DEFUN (ospf_external_route_aggregation_no_adrvertise,
str2prefix_ipv4(argv[idx]->arg, &p);
- if (is_prefix_default(&p)) {
+ if (is_default_prefix4(&p)) {
vty_out(vty,
"Default address shouldn't be configured as summary address.\n");
return CMD_SUCCESS;
@@ -10397,7 +10397,7 @@ DEFUN (no_ospf_external_route_aggregation_no_adrvertise,
str2prefix_ipv4(argv[idx]->arg, &p);
- if (is_prefix_default(&p)) {
+ if (is_default_prefix4(&p)) {
vty_out(vty,
"Default address shouldn't be configured as summary address.\n");
return CMD_SUCCESS;
@@ -11389,6 +11389,11 @@ DEFPY (clear_ip_ospf_neighbor,
if (!ospf->oi_running)
continue;
+ if (nbr_id_str && IPV4_ADDR_SAME(&ospf->router_id, &nbr_id)) {
+ vty_out(vty, "Self router-id is not allowed.\r\n ");
+ return CMD_SUCCESS;
+ }
+
ospf_neighbor_reset(ospf, nbr_id, nbr_id_str);
}
@@ -12250,6 +12255,18 @@ static int ospf_cfg_write_helper_dis_rtr_walkcb(struct hash_bucket *bucket,
return HASHWALK_CONTINUE;
}
+static void config_write_ospf_gr(struct vty *vty, struct ospf *ospf)
+{
+ if (!ospf->gr_info.restart_support)
+ return;
+
+ if (ospf->gr_info.grace_period == OSPF_DFLT_GRACE_INTERVAL)
+ vty_out(vty, " graceful-restart\n");
+ else
+ vty_out(vty, " graceful-restart grace-period %u\n",
+ ospf->gr_info.grace_period);
+}
+
static int config_write_ospf_gr_helper(struct vty *vty, struct ospf *ospf)
{
if (ospf->is_helper_supported)
@@ -12464,7 +12481,8 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf)
/* Redistribute information print. */
config_write_ospf_redistribute(vty, ospf);
- /* Print gr helper configs */
+ /* Graceful Restart print */
+ config_write_ospf_gr(vty, ospf);
config_write_ospf_gr_helper(vty, ospf);
/* Print external route aggregation. */
diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c
index 017915e0ee..1298a17f55 100644
--- a/ospfd/ospf_zebra.c
+++ b/ospfd/ospf_zebra.c
@@ -264,6 +264,14 @@ void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *p,
struct ospf_path *path;
struct listnode *node;
+ if (ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "Zebra: Graceful Restart in progress -- not installing %pFX",
+ p);
+ return;
+ }
+
memset(&api, 0, sizeof(api));
api.vrf_id = ospf->vrf_id;
api.type = ZEBRA_ROUTE_OSPF;
@@ -323,6 +331,14 @@ void ospf_zebra_delete(struct ospf *ospf, struct prefix_ipv4 *p,
{
struct zapi_route api;
+ if (ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "Zebra: Graceful Restart in progress -- not uninstalling %pFX",
+ p);
+ return;
+ }
+
memset(&api, 0, sizeof(api));
api.vrf_id = ospf->vrf_id;
api.type = ZEBRA_ROUTE_OSPF;
@@ -340,6 +356,14 @@ void ospf_zebra_add_discard(struct ospf *ospf, struct prefix_ipv4 *p)
{
struct zapi_route api;
+ if (ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "Zebra: Graceful Restart in progress -- not installing %pFX",
+ p);
+ return;
+ }
+
memset(&api, 0, sizeof(api));
api.vrf_id = ospf->vrf_id;
api.type = ZEBRA_ROUTE_OSPF;
@@ -358,6 +382,14 @@ void ospf_zebra_delete_discard(struct ospf *ospf, struct prefix_ipv4 *p)
{
struct zapi_route api;
+ if (ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "Zebra: Graceful Restart in progress -- not uninstalling %pFX",
+ p);
+ return;
+ }
+
memset(&api, 0, sizeof(api));
api.vrf_id = ospf->vrf_id;
api.type = ZEBRA_ROUTE_OSPF;
@@ -933,7 +965,7 @@ static int ospf_external_lsa_originate_check(struct ospf *ospf,
}
/* Take care of default-originate. */
- if (is_prefix_default(&ei->p))
+ if (is_default_prefix4(&ei->p))
if (ospf->default_originate == DEFAULT_ORIGINATE_NONE) {
zlog_info(
"LSA[Type5:0.0.0.0]: Not originate AS-external-LSA for default");
@@ -1089,8 +1121,8 @@ int ospf_redistribute_check(struct ospf *ospf, struct external_info *ei,
struct route_map_set_values save_values;
struct prefix_ipv4 *p = &ei->p;
struct ospf_redist *red;
- uint8_t type = is_prefix_default(&ei->p) ? DEFAULT_ROUTE : ei->type;
- unsigned short instance = is_prefix_default(&ei->p) ? 0 : ei->instance;
+ uint8_t type = is_default_prefix4(&ei->p) ? DEFAULT_ROUTE : ei->type;
+ unsigned short instance = is_default_prefix4(&ei->p) ? 0 : ei->instance;
route_tag_t saved_tag = 0;
/* Default is handled differently. */
@@ -1180,6 +1212,36 @@ void ospf_routemap_unset(struct ospf_redist *red)
ROUTEMAP(red) = NULL;
}
+static int ospf_zebra_gr_update(struct ospf *ospf, int command,
+ uint32_t stale_time)
+{
+ struct zapi_cap api;
+
+ if (!zclient || zclient->sock < 0 || !ospf)
+ return 1;
+
+ memset(&api, 0, sizeof(struct zapi_cap));
+ api.cap = command;
+ api.stale_removal_time = stale_time;
+ api.vrf_id = ospf->vrf_id;
+
+ (void)zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, zclient,
+ &api);
+
+ return 0;
+}
+
+int ospf_zebra_gr_enable(struct ospf *ospf, uint32_t stale_time)
+{
+ return ospf_zebra_gr_update(ospf, ZEBRA_CLIENT_GR_CAPABILITIES,
+ stale_time);
+}
+
+int ospf_zebra_gr_disable(struct ospf *ospf)
+{
+ return ospf_zebra_gr_update(ospf, ZEBRA_CLIENT_GR_DISABLE, 0);
+}
+
/* Zebra route add and delete treatment. */
static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS)
{
@@ -1213,7 +1275,7 @@ static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS)
* originate)ZEBRA_ROUTE_MAX is used to delete the ex-info.
* Resolved this inconsistency by maintaining same route type.
*/
- if (is_prefix_default(&p))
+ if (is_default_prefix4(&p))
rt_type = DEFAULT_ROUTE;
if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE))
@@ -1252,7 +1314,7 @@ static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS)
return 0;
}
if (ospf->router_id.s_addr != INADDR_ANY) {
- if (is_prefix_default(&p))
+ if (is_default_prefix4(&p))
ospf_external_lsa_refresh_default(ospf);
else {
struct ospf_external_aggr_rt *aggr;
@@ -1374,7 +1436,7 @@ static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS)
ospf_external_info_delete(ospf, rt_type, api.instance,
p);
- if (is_prefix_default(&p))
+ if (is_default_prefix4(&p))
ospf_external_lsa_refresh_default(ospf);
else
ospf_external_lsa_flush(ospf, rt_type, &p,
@@ -1471,7 +1533,7 @@ static int ospf_distribute_list_update_timer(struct thread *thread)
if (!ei)
continue;
- if (is_prefix_default(&ei->p))
+ if (is_default_prefix4(&ei->p))
default_refresh = 1;
else {
struct ospf_external_aggr_rt *aggr;
diff --git a/ospfd/ospf_zebra.h b/ospfd/ospf_zebra.h
index 3f4edfa29c..3bd8a7680c 100644
--- a/ospfd/ospf_zebra.h
+++ b/ospfd/ospf_zebra.h
@@ -88,6 +88,8 @@ extern int ospf_distribute_list_out_set(struct ospf *, int, const char *);
extern int ospf_distribute_list_out_unset(struct ospf *, int, const char *);
extern void ospf_routemap_set(struct ospf_redist *, const char *);
extern void ospf_routemap_unset(struct ospf_redist *);
+extern int ospf_zebra_gr_enable(struct ospf *ospf, uint32_t stale_time);
+extern int ospf_zebra_gr_disable(struct ospf *ospf);
extern int ospf_distance_set(struct vty *, struct ospf *, const char *,
const char *, const char *);
extern int ospf_distance_unset(struct vty *, struct ospf *, const char *,
diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c
index e95ee55e68..3226d66444 100644
--- a/ospfd/ospfd.c
+++ b/ospfd/ospfd.c
@@ -60,7 +60,7 @@
#include "ospfd/ospf_flood.h"
#include "ospfd/ospf_ase.h"
#include "ospfd/ospf_ldp_sync.h"
-#include "ospfd/ospf_gr_helper.h"
+#include "ospfd/ospf_gr.h"
DEFINE_QOBJ_TYPE(ospf);
@@ -420,6 +420,12 @@ static struct ospf *ospf_new(unsigned short instance, const char *name)
thread_add_read(master, ospf_read, new, new->fd, &new->t_read);
+ /*
+ * Read from non-volatile memory whether this instance is performing a
+ * graceful restart or not.
+ */
+ ospf_gr_nvm_read(new);
+
return new;
}
@@ -708,7 +714,8 @@ static void ospf_finish_final(struct ospf *ospf)
ospf_opaque_finish();
- ospf_flush_self_originated_lsas_now(ospf);
+ if (!ospf->gr_info.prepare_in_progress)
+ ospf_flush_self_originated_lsas_now(ospf);
/* Unregister redistribution */
for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
@@ -805,6 +812,7 @@ static void ospf_finish_final(struct ospf *ospf)
OSPF_TIMER_OFF(ospf->t_sr_update);
OSPF_TIMER_OFF(ospf->t_default_routemap_timer);
OSPF_TIMER_OFF(ospf->t_external_aggr);
+ OSPF_TIMER_OFF(ospf->gr_info.t_grace_period);
LSDB_LOOP (OPAQUE_AS_LSDB(ospf), rn, lsa)
ospf_discard_from_db(ospf, ospf->lsdb, lsa);
@@ -826,7 +834,8 @@ static void ospf_finish_final(struct ospf *ospf)
if (ospf->old_table)
ospf_route_table_free(ospf->old_table);
if (ospf->new_table) {
- ospf_route_delete(ospf, ospf->new_table);
+ if (!ospf->gr_info.prepare_in_progress)
+ ospf_route_delete(ospf, ospf->new_table);
ospf_route_table_free(ospf->new_table);
}
if (ospf->old_rtrs)
@@ -834,11 +843,13 @@ static void ospf_finish_final(struct ospf *ospf)
if (ospf->new_rtrs)
ospf_rtrs_free(ospf->new_rtrs);
if (ospf->new_external_route) {
- ospf_route_delete(ospf, ospf->new_external_route);
+ if (!ospf->gr_info.prepare_in_progress)
+ ospf_route_delete(ospf, ospf->new_external_route);
ospf_route_table_free(ospf->new_external_route);
}
if (ospf->old_external_route) {
- ospf_route_delete(ospf, ospf->old_external_route);
+ if (!ospf->gr_info.prepare_in_progress)
+ ospf_route_delete(ospf, ospf->old_external_route);
ospf_route_table_free(ospf->old_external_route);
}
if (ospf->external_lsas) {
diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h
index 318400e968..d64a044e14 100644
--- a/ospfd/ospfd.h
+++ b/ospfd/ospfd.h
@@ -134,6 +134,16 @@ enum protection_type {
OSPF_TI_LFA_NODE_PROTECTION,
};
+/* OSPF nonstop forwarding aka Graceful Restart */
+struct ospf_gr_info {
+ bool restart_support;
+ bool restart_in_progress;
+ bool prepare_in_progress;
+ bool finishing_restart;
+ uint32_t grace_period;
+ struct thread *t_grace_period;
+};
+
/* OSPF instance structure. */
struct ospf {
/* OSPF's running state based on the '[no] router ospf [<instance>]'
@@ -384,6 +394,9 @@ struct ospf {
/* MPLS LDP-IGP Sync */
struct ldp_sync_info_cmd ldp_sync_cmd;
+ /* OSPF Graceful Restart info */
+ struct ospf_gr_info gr_info;
+
/* TI-LFA support for all interfaces. */
bool ti_lfa_enabled;
enum protection_type ti_lfa_protection_type;
diff --git a/ospfd/subdir.am b/ospfd/subdir.am
index 574e0e3bdf..4f9cbc7b1e 100644
--- a/ospfd/subdir.am
+++ b/ospfd/subdir.am
@@ -8,6 +8,7 @@ sbin_PROGRAMS += ospfd/ospfd
vtysh_scan += \
ospfd/ospf_bfd.c \
ospfd/ospf_dump.c \
+ ospfd/ospf_gr.c \
ospfd/ospf_ldp_sync.c \
ospfd/ospf_opaque.c \
ospfd/ospf_ri.c \
@@ -35,6 +36,7 @@ ospfd_libfrrospf_a_SOURCES = \
ospfd/ospf_errors.c \
ospfd/ospf_ext.c \
ospfd/ospf_flood.c \
+ ospfd/ospf_gr.c \
ospfd/ospf_ia.c \
ospfd/ospf_interface.c \
ospfd/ospf_ism.c \
@@ -82,6 +84,7 @@ clippy_scan += \
ospfd/ospf_vty.c \
ospfd/ospf_ldp_sync.c \
ospfd/ospf_dump.c \
+ ospfd/ospf_gr.c \
# end
noinst_HEADERS += \
@@ -100,6 +103,7 @@ noinst_HEADERS += \
ospfd/ospf_network.h \
ospfd/ospf_packet.h \
ospfd/ospf_ri.h \
+ ospfd/ospf_gr.h \
ospfd/ospf_route.h \
ospfd/ospf_routemap_nb.h \
ospfd/ospf_spf.h \
@@ -108,7 +112,6 @@ noinst_HEADERS += \
ospfd/ospf_te.h \
ospfd/ospf_vty.h \
ospfd/ospf_zebra.h \
- ospfd/ospf_gr_helper.h \
# end
ospfd_ospfd_LDADD = ospfd/libfrrospf.a lib/libfrr.la $(LIBCAP) $(LIBM)
@@ -116,7 +119,7 @@ ospfd_ospfd_SOURCES = ospfd/ospf_main.c
ospfd_ospfd_snmp_la_SOURCES = ospfd/ospf_snmp.c
ospfd_ospfd_snmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
-ospfd_ospfd_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+ospfd_ospfd_snmp_la_LDFLAGS = $(MODULE_LDFLAGS)
ospfd_ospfd_snmp_la_LIBADD = lib/libfrrsnmp.la
EXTRA_DIST += \
diff --git a/pathd/path_pcep_cli.c b/pathd/path_pcep_cli.c
index 2e4e331ad9..a911210ab4 100644
--- a/pathd/path_pcep_cli.c
+++ b/pathd/path_pcep_cli.c
@@ -505,7 +505,7 @@ static int path_pcep_cli_show_srte_pcep_counters(struct vty *vty)
{
int i, j, row;
time_t diff_time;
- struct tm *tm_info;
+ struct tm tm_info;
char tm_buffer[26];
struct counters_group *group;
struct counters_subgroup *subgroup;
@@ -522,8 +522,8 @@ static int path_pcep_cli_show_srte_pcep_counters(struct vty *vty)
}
diff_time = time(NULL) - group->start_time;
- tm_info = localtime(&group->start_time);
- strftime(tm_buffer, sizeof(tm_buffer), "%Y-%m-%d %H:%M:%S", tm_info);
+ localtime_r(&group->start_time, &tm_info);
+ strftime(tm_buffer, sizeof(tm_buffer), "%Y-%m-%d %H:%M:%S", &tm_info);
vty_out(vty, "PCEP counters since %s (%uh %um %us):\n", tm_buffer,
(uint32_t)(diff_time / 3600), (uint32_t)((diff_time / 60) % 60),
diff --git a/pathd/subdir.am b/pathd/subdir.am
index 693afabb39..f339c79225 100644
--- a/pathd/subdir.am
+++ b/pathd/subdir.am
@@ -82,4 +82,4 @@ endif
#pathd_pathd_pcep_la_CFLAGS = $(AM_CFLAGS)
-pathd_pathd_pcep_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+pathd_pathd_pcep_la_LDFLAGS = $(MODULE_LDFLAGS)
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index cc11a3cc17..37d206cc11 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -497,6 +497,7 @@ static void igmp_show_interfaces(struct pim_instance *pim, struct vty *vty,
struct interface *ifp;
time_t now;
char buf[PREFIX_STRLEN];
+ char quer_buf[PREFIX_STRLEN];
json_object *json = NULL;
json_object *json_row = NULL;
@@ -506,7 +507,7 @@ static void igmp_show_interfaces(struct pim_instance *pim, struct vty *vty,
json = json_object_new_object();
else
vty_out(vty,
- "Interface State Address V Querier Query Timer Uptime\n");
+ "Interface State Address V Querier QuerierIp Query Timer Uptime\n");
FOR_ALL_INTERFACES (pim->vrf, ifp) {
struct pim_interface *pim_ifp;
@@ -544,6 +545,10 @@ static void igmp_show_interfaces(struct pim_instance *pim, struct vty *vty,
"queryTimer",
query_hhmmss);
}
+ json_object_string_add(
+ json_row, "querierIp",
+ inet_ntop(AF_INET, &igmp->querier_addr,
+ quer_buf, sizeof(quer_buf)));
json_object_object_add(json, ifp->name,
json_row);
@@ -554,18 +559,19 @@ static void igmp_show_interfaces(struct pim_instance *pim, struct vty *vty,
}
} else {
vty_out(vty,
- "%-16s %5s %15s %d %7s %11s %8s\n",
+ "%-16s %5s %15s %d %7s %17pI4 %11s %8s\n",
ifp->name,
if_is_up(ifp)
- ? (igmp->mtrace_only ? "mtrc"
- : "up")
- : "down",
- inet_ntop(AF_INET, &igmp->ifaddr,
- buf, sizeof(buf)),
+ ? (igmp->mtrace_only ? "mtrc"
+ : "up")
+ : "down",
+ inet_ntop(AF_INET, &igmp->ifaddr, buf,
+ sizeof(buf)),
pim_ifp->igmp_version,
igmp->t_igmp_query_timer ? "local"
- : "other",
- query_hhmmss, uptime);
+ : "other",
+ &igmp->querier_addr, query_hhmmss,
+ uptime);
}
}
}
@@ -586,6 +592,7 @@ static void igmp_show_interfaces_single(struct pim_instance *pim,
struct listnode *sock_node;
struct pim_interface *pim_ifp;
char uptime[10];
+ char quer_buf[PREFIX_STRLEN];
char query_hhmmss[10];
char other_hhmmss[10];
int found_ifname = 0;
@@ -670,6 +677,10 @@ static void igmp_show_interfaces_single(struct pim_instance *pim,
igmp->t_igmp_query_timer
? "local"
: "other");
+ json_object_string_add(
+ json_row, "querierIp",
+ inet_ntop(AF_INET, &igmp->querier_addr,
+ quer_buf, sizeof(quer_buf)));
json_object_int_add(json_row, "queryStartCount",
igmp->startup_query_count);
json_object_string_add(json_row,
@@ -739,6 +750,14 @@ static void igmp_show_interfaces_single(struct pim_instance *pim,
vty_out(vty, "Querier : %s\n",
igmp->t_igmp_query_timer ? "local"
: "other");
+ vty_out(vty, "QuerierIp : %pI4",
+ &igmp->querier_addr);
+ if (pim_ifp->primary_address.s_addr
+ == igmp->querier_addr.s_addr)
+ vty_out(vty, " (this router)\n");
+ else
+ vty_out(vty, "\n");
+
vty_out(vty, "Start Count : %d\n",
igmp->startup_query_count);
vty_out(vty, "Query Timer : %s\n",
@@ -1135,6 +1154,12 @@ static void pim_show_interfaces_single(struct pim_instance *pim,
json_object_int_add(
json_row, "overrideIntervalHighest",
pim_ifp->pim_neighbors_highest_override_interval_msec);
+ if (pim_ifp->bsm_enable)
+ json_object_boolean_true_add(json_row,
+ "bsmEnabled");
+ if (pim_ifp->ucast_bsm_accept)
+ json_object_boolean_true_add(json_row,
+ "ucastBsmEnabled");
json_object_object_add(json, ifp->name, json_row);
} else {
@@ -1289,6 +1314,15 @@ static void pim_show_interfaces_single(struct pim_instance *pim,
pim_ifp->pim_neighbors_highest_override_interval_msec);
vty_out(vty, "\n");
vty_out(vty, "\n");
+
+ vty_out(vty, "BSM Status\n");
+ vty_out(vty, "----------\n");
+ vty_out(vty, "Bsm Enabled : %s\n",
+ pim_ifp->bsm_enable ? "yes" : "no");
+ vty_out(vty, "Unicast Bsm Enabled : %s\n",
+ pim_ifp->ucast_bsm_accept ? "yes" : "no");
+ vty_out(vty, "\n");
+ vty_out(vty, "\n");
}
}
@@ -7153,7 +7187,7 @@ DEFPY (pim_register_accept_list,
DEFUN (ip_pim_joinprune_time,
ip_pim_joinprune_time_cmd,
- "ip pim join-prune-interval (60-600)",
+ "ip pim join-prune-interval (5-600)",
IP_STR
"pim multicast routing\n"
"Join Prune Send Interval\n"
@@ -7167,7 +7201,7 @@ DEFUN (ip_pim_joinprune_time,
DEFUN (no_ip_pim_joinprune_time,
no_ip_pim_joinprune_time_cmd,
- "no ip pim join-prune-interval (60-600)",
+ "no ip pim join-prune-interval (5-600)",
NO_STR
IP_STR
"pim multicast routing\n"
@@ -8752,7 +8786,7 @@ DEFUN (interface_no_ip_mroute,
DEFUN (interface_ip_pim_hello,
interface_ip_pim_hello_cmd,
- "ip pim hello (1-180) [(1-180)]",
+ "ip pim hello (1-180) [(1-630)]",
IP_STR
PIM_STR
IFACE_PIM_HELLO_STR
@@ -8787,7 +8821,7 @@ DEFUN (interface_ip_pim_hello,
DEFUN (interface_no_ip_pim_hello,
interface_no_ip_pim_hello_cmd,
- "no ip pim hello [(1-180) [(1-180)]]",
+ "no ip pim hello [(1-180) [(1-630)]]",
NO_STR
IP_STR
PIM_STR
diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c
index 48b019c8c8..0b28a3e84c 100644
--- a/pimd/pim_iface.c
+++ b/pimd/pim_iface.c
@@ -1512,10 +1512,15 @@ struct prefix *pim_if_connected_to_source(struct interface *ifp, struct in_addr
p.prefixlen = IPV4_MAX_BITLEN;
for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) {
- if ((c->address->family == AF_INET)
- && prefix_match(CONNECTED_PREFIX(c), &p)) {
- return CONNECTED_PREFIX(c);
- }
+ if (c->address->family != AF_INET)
+ continue;
+ if (prefix_match(c->address, &p))
+ return c->address;
+ if (CONNECTED_PEER(c) && prefix_match(c->destination, &p))
+ /* this is not a typo, on PtP we need to return the
+ * *local* address that lines up with src.
+ */
+ return c->address;
}
return NULL;
diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c
index 73dcdbddb4..426112d4d6 100644
--- a/pimd/pim_igmp.c
+++ b/pimd/pim_igmp.c
@@ -167,6 +167,8 @@ static int pim_igmp_other_querier_expire(struct thread *t)
sizeof(ifaddr_str));
zlog_debug("%s: Querier %s resuming", __func__, ifaddr_str);
}
+ /* Mark the interface address as querier address */
+ igmp->querier_addr = igmp->ifaddr;
/*
We are the current querier, then
@@ -397,6 +399,8 @@ static int igmp_recv_query(struct igmp_sock *igmp, int query_version,
ntohl(igmp->ifaddr.s_addr), from_str,
ntohl(from.s_addr));
}
+ if (ntohl(from.s_addr) < ntohl(igmp->querier_addr.s_addr))
+ igmp->querier_addr.s_addr = from.s_addr;
pim_igmp_other_querier_timer_on(igmp);
}
@@ -469,47 +473,83 @@ static int igmp_v1_recv_report(struct igmp_sock *igmp, struct in_addr from,
return 0;
}
-int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
+bool pim_igmp_verify_header(struct ip *ip_hdr, size_t len, size_t *hlen)
{
- struct ip *ip_hdr;
- size_t ip_hlen; /* ip header length in bytes */
char *igmp_msg;
int igmp_msg_len;
int msg_type;
- char from_str[INET_ADDRSTRLEN];
- char to_str[INET_ADDRSTRLEN];
+ size_t ip_hlen; /* ip header length in bytes */
if (len < sizeof(*ip_hdr)) {
zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", len,
sizeof(*ip_hdr));
- return -1;
+ return false;
}
- ip_hdr = (struct ip *)buf;
-
- pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str, sizeof(from_str));
- pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str, sizeof(to_str));
-
ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
+ *hlen = ip_hlen;
if (ip_hlen > len) {
zlog_warn(
"IGMP packet header claims size %zu, but we only have %zu bytes",
ip_hlen, len);
- return -1;
+ return false;
}
- igmp_msg = buf + ip_hlen;
+ igmp_msg = (char *)ip_hdr + ip_hlen;
igmp_msg_len = len - ip_hlen;
+ msg_type = *igmp_msg;
if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
zlog_warn("IGMP message size=%d shorter than minimum=%d",
igmp_msg_len, PIM_IGMP_MIN_LEN);
- return -1;
+ return false;
+ }
+
+ if ((msg_type != PIM_IGMP_MTRACE_RESPONSE)
+ && (msg_type != PIM_IGMP_MTRACE_QUERY_REQUEST)) {
+ if (ip_hdr->ip_ttl != 1) {
+ zlog_warn(
+ "Recv IGMP packet with invalid ttl=%u, discarding the packet",
+ ip_hdr->ip_ttl);
+ return -1;
+ }
}
+ if ((msg_type == PIM_IGMP_V3_MEMBERSHIP_REPORT)
+ || ((msg_type == PIM_IGMP_MEMBERSHIP_QUERY)
+ && (igmp_msg_len >= IGMP_V3_SOURCES_OFFSET))) {
+ /* All IGMPv3 messages must be received with TOS set to 0xC0*/
+ if (ip_hdr->ip_tos != IPTOS_PREC_INTERNETCONTROL) {
+ zlog_warn("Received IGMP Packet with invalid TOS %u",
+ ip_hdr->ip_tos);
+ return -1;
+ }
+ }
+
+ return true;
+}
+
+int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
+{
+ struct ip *ip_hdr = (struct ip *)buf;
+ size_t ip_hlen; /* ip header length in bytes */
+ char *igmp_msg;
+ int igmp_msg_len;
+ int msg_type;
+ char from_str[INET_ADDRSTRLEN];
+ char to_str[INET_ADDRSTRLEN];
+
+ if (!pim_igmp_verify_header(ip_hdr, len, &ip_hlen))
+ return -1;
+
+ igmp_msg = buf + ip_hlen;
+ igmp_msg_len = len - ip_hlen;
msg_type = *igmp_msg;
+ pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str, sizeof(from_str));
+ pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str, sizeof(to_str));
+
if (PIM_DEBUG_IGMP_PACKETS) {
zlog_debug(
"Recv IGMP packet from %s to %s on %s: size=%zu ttl=%d msg_type=%d msg_size=%d",
@@ -935,6 +975,7 @@ static struct igmp_sock *igmp_sock_new(int fd, struct in_addr ifaddr,
igmp->fd = fd;
igmp->interface = ifp;
igmp->ifaddr = ifaddr;
+ igmp->querier_addr = ifaddr;
igmp->t_igmp_read = NULL;
igmp->t_igmp_query_timer = NULL;
igmp->t_other_querier_timer = NULL; /* no other querier present */
diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h
index a0681128c0..abb8af836b 100644
--- a/pimd/pim_igmp.h
+++ b/pimd/pim_igmp.h
@@ -92,8 +92,8 @@ struct igmp_sock {
struct thread
*t_igmp_query_timer; /* timer: issue IGMP general queries */
struct thread *t_other_querier_timer; /* timer: other querier present */
-
- int querier_query_interval; /* QQI */
+ struct in_addr querier_addr; /* IP address of the querier */
+ int querier_query_interval; /* QQI */
int querier_robustness_variable; /* QRV */
int startup_query_count;
@@ -116,7 +116,7 @@ void igmp_sock_delete(struct igmp_sock *igmp);
void igmp_sock_free(struct igmp_sock *igmp);
void igmp_sock_delete_all(struct interface *ifp);
int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len);
-
+bool pim_igmp_verify_header(struct ip *ip_hdr, size_t len, size_t *ip_hlen);
void pim_igmp_general_query_on(struct igmp_sock *igmp);
void pim_igmp_general_query_off(struct igmp_sock *igmp);
void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp);
diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c
index dfdbd6dee2..bd5e215027 100644
--- a/pimd/pim_nb_config.c
+++ b/pimd/pim_nb_config.c
@@ -1613,7 +1613,7 @@ int lib_interface_pim_hello_holdtime_modify(struct nb_cb_modify_args *args)
ifp = nb_running_get_entry(args->dnode, NULL, true);
pim_ifp = ifp->info;
pim_ifp->pim_default_holdtime =
- yang_dnode_get_uint8(args->dnode, NULL);
+ yang_dnode_get_uint16(args->dnode, NULL);
break;
}
diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c
index 48b1a30f2d..571173c62a 100644
--- a/pimd/pim_neighbor.c
+++ b/pimd/pim_neighbor.c
@@ -350,8 +350,8 @@ pim_neighbor_new(struct interface *ifp, struct in_addr source_addr,
__func__, src_str, ifp->name);
}
- zlog_info("PIM NEIGHBOR UP: neighbor %s on interface %s", src_str,
- ifp->name);
+ zlog_notice("PIM NEIGHBOR UP: neighbor %s on interface %s", src_str,
+ ifp->name);
if (neigh->propagation_delay_msec
> pim_ifp->pim_neighbors_highest_propagation_delay_msec) {
@@ -616,8 +616,8 @@ void pim_neighbor_delete(struct interface *ifp, struct pim_neighbor *neigh,
assert(pim_ifp);
pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
- zlog_info("PIM NEIGHBOR DOWN: neighbor %s on interface %s: %s", src_str,
- ifp->name, delete_message);
+ zlog_notice("PIM NEIGHBOR DOWN: neighbor %s on interface %s: %s",
+ src_str, ifp->name, delete_message);
THREAD_OFF(neigh->t_expire_timer);
diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c
index b6521132f7..56e1927528 100644
--- a/pimd/pim_rp.c
+++ b/pimd/pim_rp.c
@@ -204,6 +204,26 @@ static struct rp_info *pim_rp_find_exact(struct pim_instance *pim,
}
/*
+ * XXX: long-term issue: we don't actually have a good "ip address-list"
+ * implementation. ("access-list XYZ" is the closest but honestly it's
+ * kinda garbage.)
+ *
+ * So it's using a prefix-list to match an address here, which causes very
+ * unexpected results for the user since prefix-lists by default only match
+ * when the prefix length is an exact match too. i.e. you'd have to add the
+ * "le 32" and do "ip prefix-list foo permit 10.0.0.0/24 le 32"
+ *
+ * To avoid this pitfall, this code uses "address_mode = true" for the prefix
+ * list match (this is the only user for that.)
+ *
+ * In the long run, we need to add a "ip address-list", but that's a wholly
+ * separate bag of worms, and existing configs using ip prefix-list would
+ * drop into the UX pitfall.
+ */
+
+#include "lib/plist_int.h"
+
+/*
* Given a group, return the rp_info for that group
*/
struct rp_info *pim_rp_find_match_group(struct pim_instance *pim,
@@ -213,7 +233,8 @@ struct rp_info *pim_rp_find_match_group(struct pim_instance *pim,
struct rp_info *best = NULL;
struct rp_info *rp_info;
struct prefix_list *plist;
- const struct prefix *p, *bp;
+ const struct prefix *bp;
+ const struct prefix_list_entry *entry;
struct route_node *rn;
bp = NULL;
@@ -221,19 +242,19 @@ struct rp_info *pim_rp_find_match_group(struct pim_instance *pim,
if (rp_info->plist) {
plist = prefix_list_lookup(AFI_IP, rp_info->plist);
- if (prefix_list_apply_which_prefix(plist, &p, group)
- == PREFIX_DENY)
+ if (prefix_list_apply_ext(plist, &entry, group, true)
+ == PREFIX_DENY || !entry)
continue;
if (!best) {
best = rp_info;
- bp = p;
+ bp = &entry->prefix;
continue;
}
- if (bp && bp->prefixlen < p->prefixlen) {
+ if (bp && bp->prefixlen < entry->prefix.prefixlen) {
best = rp_info;
- bp = p;
+ bp = &entry->prefix;
}
}
}
diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c
index 504519c8a4..05b0f92a4b 100644
--- a/pimd/pim_sock.c
+++ b/pimd/pim_sock.c
@@ -112,17 +112,15 @@ int pim_socket_mcast(int protocol, struct in_addr ifaddr, struct interface *ifp,
}
#ifdef SO_BINDTODEVICE
- if (protocol == IPPROTO_PIM) {
- int ret;
+ int ret;
- ret = pim_socket_bind(fd, ifp);
- if (ret) {
- close(fd);
- zlog_warn(
- "Could not set fd: %d for interface: %s to device",
- fd, ifp->name);
- return PIM_SOCK_ERR_BIND;
- }
+ ret = pim_socket_bind(fd, ifp);
+ if (ret) {
+ close(fd);
+ zlog_warn(
+ "Could not set fd: %d for interface: %s to device",
+ fd, ifp->name);
+ return PIM_SOCK_ERR_BIND;
}
#else
/* XXX: use IP_PKTINFO / IP_RECVIF to emulate behaviour? Or change to
diff --git a/qpb/subdir.am b/qpb/subdir.am
index 704efc5930..e897822ecc 100644
--- a/qpb/subdir.am
+++ b/qpb/subdir.am
@@ -4,7 +4,7 @@ endif
qpb_libfrr_pb_la_CPPFLAGS = $(AM_CPPFLAGS) $(PROTOBUF_C_CFLAGS)
qpb_libfrr_pb_la_LIBADD = $(PROTOBUF_C_LIBS)
-qpb_libfrr_pb_la_LDFLAGS = -version-info 0:0:0
+qpb_libfrr_pb_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
qpb_libfrr_pb_la_SOURCES = \
qpb/qpb.c \
diff --git a/ripd/subdir.am b/ripd/subdir.am
index 8de0fc4b5a..b43e369ab2 100644
--- a/ripd/subdir.am
+++ b/ripd/subdir.am
@@ -57,5 +57,5 @@ nodist_ripd_ripd_SOURCES = \
ripd_ripd_snmp_la_SOURCES = ripd/rip_snmp.c
ripd_ripd_snmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
-ripd_ripd_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+ripd_ripd_snmp_la_LDFLAGS = $(MODULE_LDFLAGS)
ripd_ripd_snmp_la_LIBADD = lib/libfrrsnmp.la
diff --git a/staticd/static_routes.c b/staticd/static_routes.c
index cdafc4a76a..58a29ad25b 100644
--- a/staticd/static_routes.c
+++ b/staticd/static_routes.c
@@ -278,6 +278,9 @@ static_add_nexthop(struct route_node *rn, struct static_path *pn, safi_t safi,
nh->type = type;
nh->color = color;
+ if (nh->type == STATIC_BLACKHOLE)
+ nh->bh_type = STATIC_BLACKHOLE_NULL;
+
nh->nh_vrf_id = nh_svrf ? nh_svrf->vrf->vrf_id : VRF_UNKNOWN;
strlcpy(nh->nh_vrfname, nh_vrf, sizeof(nh->nh_vrfname));
@@ -325,6 +328,7 @@ static_add_nexthop(struct route_node *rn, struct static_path *pn, safi_t safi,
switch (nh->type) {
case STATIC_IPV4_GATEWAY:
case STATIC_IPV6_GATEWAY:
+ case STATIC_BLACKHOLE:
break;
case STATIC_IPV4_GATEWAY_IFNAME:
case STATIC_IPV6_GATEWAY_IFNAME:
@@ -337,9 +341,6 @@ static_add_nexthop(struct route_node *rn, struct static_path *pn, safi_t safi,
ifname);
break;
- case STATIC_BLACKHOLE:
- nh->bh_type = STATIC_BLACKHOLE_NULL;
- break;
case STATIC_IFNAME:
ifp = if_lookup_by_name(ifname, nh->nh_vrf_id);
if (ifp && ifp->ifindex != IFINDEX_INTERNAL) {
diff --git a/tests/.gitignore b/tests/.gitignore
index 3fad1b0813..498d7dd0b7 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -36,6 +36,7 @@
/lib/test_nexthop
/lib/test_nexthop_iter
/lib/test_ntop
+/lib/test_plist
/lib/test_prefix2str
/lib/test_printfrr
/lib/test_privs
diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c
index 49bc0f4fb2..8be81cc4cb 100644
--- a/tests/lib/cli/common_cli.c
+++ b/tests/lib/cli/common_cli.c
@@ -59,10 +59,13 @@ static void vty_do_exit(int isexit)
exit(0);
}
+const struct frr_yang_module_info *const *test_yang_modules = NULL;
+
/* main routine. */
int main(int argc, char **argv)
{
struct thread thread;
+ size_t yangcount;
/* Set umask before anything for security */
umask(0027);
@@ -79,7 +82,11 @@ int main(int argc, char **argv)
vty_init(master, false);
lib_cmd_init();
- nb_init(master, NULL, 0, false);
+
+ for (yangcount = 0; test_yang_modules && test_yang_modules[yangcount];
+ yangcount++)
+ ;
+ nb_init(master, test_yang_modules, yangcount, false);
test_init(argc, argv);
diff --git a/tests/lib/cli/common_cli.h b/tests/lib/cli/common_cli.h
index 15abe3b855..3042ff5b12 100644
--- a/tests/lib/cli/common_cli.h
+++ b/tests/lib/cli/common_cli.h
@@ -25,6 +25,9 @@
#include "zebra.h"
#include "vty.h"
#include "command.h"
+#include "northbound.h"
+
+extern const struct frr_yang_module_info *const *test_yang_modules;
/* function to be implemented by test */
extern void test_init(int argc, char **argv);
diff --git a/tests/lib/test_plist.c b/tests/lib/test_plist.c
new file mode 100644
index 0000000000..ee7a9ebf30
--- /dev/null
+++ b/tests/lib/test_plist.c
@@ -0,0 +1,48 @@
+/*
+ * Simple prefix list querying tool
+ *
+ * Copyright (C) 2021 by David Lamparter,
+ * for Open Source Routing / NetDEF, Inc.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "lib/plist.h"
+#include "lib/filter.h"
+#include "tests/lib/cli/common_cli.h"
+
+static const struct frr_yang_module_info *const my_yang_modules[] = {
+ &frr_filter_info,
+ NULL,
+};
+
+__attribute__((_CONSTRUCTOR(2000)))
+static void test_yang_modules_set(void)
+{
+ test_yang_modules = my_yang_modules;
+}
+
+void test_init(int argc, char **argv)
+{
+ prefix_list_init();
+ filter_cli_init();
+
+ /* nothing else to do here, giving stand-alone access to the prefix
+ * list code's "debug prefix-list ..." command is the only purpose of
+ * this "test".
+ */
+}
diff --git a/tests/subdir.am b/tests/subdir.am
index c2153140f5..86c1aa4284 100644
--- a/tests/subdir.am
+++ b/tests/subdir.am
@@ -87,6 +87,7 @@ check_PROGRAMS = \
tests/lib/test_nexthop_iter \
tests/lib/test_nexthop \
tests/lib/test_ntop \
+ tests/lib/test_plist \
tests/lib/test_prefix2str \
tests/lib/test_printfrr \
tests/lib/test_privs \
@@ -344,6 +345,10 @@ tests_lib_test_ntop_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_ntop_CPPFLAGS = $(CPPFLAGS_BASE) # no assert override
tests_lib_test_ntop_LDADD = # none
tests_lib_test_ntop_SOURCES = tests/lib/test_ntop.c tests/helpers/c/prng.c
+tests_lib_test_plist_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_plist_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_plist_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_plist_SOURCES = tests/lib/test_plist.c tests/lib/cli/common_cli.c
tests_lib_test_prefix2str_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_prefix2str_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_prefix2str_LDADD = $(ALL_TESTS_LDADD)
diff --git a/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py b/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py
index 374cce21f6..485a76c6b2 100644
--- a/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py
+++ b/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py
@@ -774,9 +774,9 @@ def test_BGP_attributes_with_vrf_default_keyword_p0(request):
}
result = verify_bgp_rib(tgen, addr_type, dut, input_dict)
- assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
result = verify_rib(tgen, addr_type, dut, input_dict)
- assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
for addr_type in ADDR_TYPES:
dut = "r4"
@@ -793,9 +793,9 @@ def test_BGP_attributes_with_vrf_default_keyword_p0(request):
}
result = verify_bgp_rib(tgen, addr_type, dut, input_dict)
- assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
result = verify_rib(tgen, addr_type, dut, input_dict)
- assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
input_dict_4 = {"largeCommunity": "500:500:500", "community": "500:500"}
@@ -1134,15 +1134,10 @@ def test_bgp_with_loopback_with_same_subnet_p1(request):
dut = "r1"
protocol = "bgp"
for addr_type in ADDR_TYPES:
- result = verify_rib(tgen, addr_type, dut, input_dict_r1, protocol=protocol)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- result = verify_fib_routes(tgen, addr_type, dut, input_dict_r1, expected=False)
- assert result is not True, "Testcase {} : Failed \n"
+ result = verify_fib_routes(tgen, addr_type, dut, input_dict_r1)
+ assert result is not True, "Testcase {} : Failed \n".format(tc_name)
"Expected behavior: routes should not present in fib \n"
- "Error: {}".format(tc_name, result)
+ "Error: {}".format(result)
step("Verify Ipv4 and Ipv6 network installed in r3 RIB but not in FIB")
input_dict_r3 = {
@@ -1156,17 +1151,10 @@ def test_bgp_with_loopback_with_same_subnet_p1(request):
dut = "r3"
protocol = "bgp"
for addr_type in ADDR_TYPES:
- result = verify_rib(
- tgen, addr_type, dut, input_dict_r3, protocol=protocol, fib=None
- )
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- result = verify_fib_routes(tgen, addr_type, dut, input_dict_r1, expected=False)
- assert result is not True, "Testcase {} : Failed \n"
+ result = verify_fib_routes(tgen, addr_type, dut, input_dict_r1)
+ assert result is not True, "Testcase {} : Failed \n".format(tc_name)
"Expected behavior: routes should not present in fib \n"
- "Error: {}".format(tc_name, result)
+ "Error: {}".format(result)
write_test_footer(tc_name)
diff --git a/tests/topotests/bgp_community_alias/r1/bgpd.conf b/tests/topotests/bgp_community_alias/r1/bgpd.conf
index 06113bdd2a..a6366204e8 100644
--- a/tests/topotests/bgp_community_alias/r1/bgpd.conf
+++ b/tests/topotests/bgp_community_alias/r1/bgpd.conf
@@ -6,4 +6,17 @@ bgp community alias 65001:1:1 large-community-r2-1
router bgp 65001
no bgp ebgp-requires-policy
neighbor 192.168.1.2 remote-as external
+ address-family ipv4 unicast
+ redistribute connected
+ neighbor 192.168.1.2 route-map r2 in
+ exit-address-family
+!
+route-map r2 permit 10
+ match alias community-r2-1
+ set tag 10
+route-map r2 permit 20
+ match alias community-r2-2
+ set tag 20
+route-map r2 permit 30
+ set tag 100
!
diff --git a/tests/topotests/bgp_community_alias/r2/bgpd.conf b/tests/topotests/bgp_community_alias/r2/bgpd.conf
index fc67ff2ad2..9276fe592d 100644
--- a/tests/topotests/bgp_community_alias/r2/bgpd.conf
+++ b/tests/topotests/bgp_community_alias/r2/bgpd.conf
@@ -8,6 +8,7 @@ router bgp 65002
!
ip prefix-list p1 permit 172.16.16.1/32
ip prefix-list p2 permit 172.16.16.2/32
+ip prefix-list p3 permit 172.16.16.3/32
!
route-map r1 permit 10
match ip address prefix-list p1
@@ -16,4 +17,6 @@ route-map r1 permit 10
route-map r1 permit 20
match ip address prefix-list p2
set community 65002:1 65002:2
+route-map r1 permit 30
+ match ip address prefix-list p3
!
diff --git a/tests/topotests/bgp_community_alias/r2/zebra.conf b/tests/topotests/bgp_community_alias/r2/zebra.conf
index a806628a8e..b8cb9baf3c 100644
--- a/tests/topotests/bgp_community_alias/r2/zebra.conf
+++ b/tests/topotests/bgp_community_alias/r2/zebra.conf
@@ -2,6 +2,7 @@
int lo
ip address 172.16.16.1/32
ip address 172.16.16.2/32
+ ip address 172.16.16.3/32
!
int r2-eth0
ip address 192.168.1.2/24
diff --git a/tests/topotests/bgp_community_alias/test_bgp-community-alias.py b/tests/topotests/bgp_community_alias/test_bgp-community-alias.py
index 90eeaaa731..c41ba810f1 100644
--- a/tests/topotests/bgp_community_alias/test_bgp-community-alias.py
+++ b/tests/topotests/bgp_community_alias/test_bgp-community-alias.py
@@ -84,39 +84,57 @@ def test_bgp_community_alias():
router = tgen.gears["r1"]
def _bgp_converge(router):
- output = json.loads(
- router.vtysh_cmd("show bgp ipv4 unicast 172.16.16.1/32 json")
- )
+ output = json.loads(router.vtysh_cmd("show ip route json"))
expected = {
- "paths": [
+ "172.16.16.1/32": [
+ {
+ "tag": 10,
+ "communities": "community-r2-1 65001:2",
+ "largeCommunities": "large-community-r2-1 65001:1:2",
+ }
+ ],
+ "172.16.16.2/32": [
+ {
+ "tag": 20,
+ "communities": "65002:1 community-r2-2",
+ "largeCommunities": "",
+ }
+ ],
+ "172.16.16.3/32": [
{
- "community": {"string": "community-r2-1 65001:2"},
- "largeCommunity": {"string": "large-community-r2-1 65001:1:2"},
+ "tag": 100,
+ "communities": "",
+ "largeCommunities": "",
}
- ]
+ ],
}
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_converge, router)
success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
- assert result is None, 'Cannot see BGP community aliases "{}"'.format(router)
+ assert result is None, "Cannot see BGP community aliases at r1"
def _bgp_show_prefixes_by_alias(router):
output = json.loads(
- router.vtysh_cmd("show bgp ipv4 unicast alias community-r2-2 json detail")
+ router.vtysh_cmd(
+ "show bgp ipv4 unicast alias large-community-r2-1 json detail"
+ )
)
expected = {
"routes": {
- "172.16.16.2/32": [{"community": {"string": "65002:1 community-r2-2"}}]
+ "172.16.16.1/32": [
+ {
+ "community": {"string": "community-r2-1 65001:2"},
+ "largeCommunity": {"string": "large-community-r2-1 65001:1:2"},
+ }
+ ]
}
}
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_show_prefixes_by_alias, router)
success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
- assert result is None, 'Cannot see BGP prefixes by community alias "{}"'.format(
- router
- )
+ assert result is None, "Cannot see BGP prefixes by community alias at r1"
if __name__ == "__main__":
diff --git a/tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf b/tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf
index 633d1832fd..293b38c7e8 100644
--- a/tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf
+++ b/tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf
@@ -17,6 +17,7 @@ route-map DEF permit 10
!
router bgp 1
bgp log-neighbor-changes
+ bgp conditional-advertisement timer 5
no bgp ebgp-requires-policy
neighbor 10.10.10.2 remote-as 2
!
diff --git a/tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf b/tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf
index c6147fe658..82525fac64 100644
--- a/tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf
+++ b/tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf
@@ -32,6 +32,7 @@ route-map RMAP-2 deny 10
!
router bgp 2
bgp log-neighbor-changes
+ bgp conditional-advertisement timer 5
no bgp ebgp-requires-policy
neighbor 10.10.10.1 remote-as 1
neighbor 10.10.20.3 remote-as 3
diff --git a/tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf b/tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf
index 2f4f5068d8..f389f309a6 100644
--- a/tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf
+++ b/tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf
@@ -1,6 +1,7 @@
!
router bgp 3
bgp log-neighbor-changes
+ bgp conditional-advertisement timer 5
no bgp ebgp-requires-policy
neighbor 10.10.20.2 remote-as 2
!
diff --git a/tests/topotests/bgp_default_route/r2/bgpd.conf b/tests/topotests/bgp_default_route/r2/bgpd.conf
index 00c96cc58b..6d1080c119 100644
--- a/tests/topotests/bgp_default_route/r2/bgpd.conf
+++ b/tests/topotests/bgp_default_route/r2/bgpd.conf
@@ -2,7 +2,4 @@ router bgp 65001
no bgp ebgp-requires-policy
neighbor 192.168.255.1 remote-as 65000
neighbor 192.168.255.1 timers 3 10
- address-family ipv4 unicast
- redistribute connected
- exit-address-family
!
diff --git a/tests/topotests/bgp_default_route/test_bgp_default-originate.py b/tests/topotests/bgp_default_route/test_bgp_default-originate.py
index d8de0f0ac6..19632162b4 100644
--- a/tests/topotests/bgp_default_route/test_bgp_default-originate.py
+++ b/tests/topotests/bgp_default_route/test_bgp_default-originate.py
@@ -79,10 +79,10 @@ def test_bgp_default_originate_route_map():
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
- router = tgen.gears["r2"]
-
- def _bgp_converge(router):
- output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ def _bgp_check_if_received():
+ output = json.loads(
+ tgen.gears["r2"].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")
+ )
expected = {
"192.168.255.1": {
"bgpState": "Established",
@@ -91,22 +91,27 @@ def test_bgp_default_originate_route_map():
}
return topotest.json_cmp(output, expected)
+ def _bgp_check_if_originated():
+ output = json.loads(tgen.gears["r1"].vtysh_cmd("show ip bgp summary json"))
+ expected = {"ipv4Unicast": {"peers": {"192.168.255.2": {"pfxSnt": 1}}}}
+ return topotest.json_cmp(output, expected)
+
def _bgp_default_route_is_valid(router):
output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json"))
expected = {"paths": [{"valid": True}]}
return topotest.json_cmp(output, expected)
- test_func = functools.partial(_bgp_converge, router)
+ test_func = functools.partial(_bgp_check_if_received)
success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "No 0.0.0.0/0 at r2 from r1"
- assert result is None, 'Failed to see bgp convergence in "{}"'.format(router)
-
- test_func = functools.partial(_bgp_default_route_is_valid, router)
+ test_func = functools.partial(_bgp_check_if_originated)
success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "No 0.0.0.0/0 from r1 to r2"
- assert (
- result is None
- ), 'Failed to see applied metric for default route in "{}"'.format(router)
+ test_func = functools.partial(_bgp_default_route_is_valid, tgen.gears["r2"])
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Failed to see 0.0.0.0/0 in r2"
if __name__ == "__main__":
diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py b/tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py
index 086bad6481..fd5bb38b98 100755
--- a/tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py
@@ -365,6 +365,10 @@ def test_ip_pe1_learn():
"run the IP learn test for PE1"
tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
host1 = tgen.gears["host1"]
pe1 = tgen.gears["PE1"]
pe2 = tgen.gears["PE2"]
@@ -380,6 +384,10 @@ def test_ip_pe2_learn():
"run the IP learn test for PE2"
tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
host2 = tgen.gears["host2"]
pe1 = tgen.gears["PE1"]
pe2 = tgen.gears["PE2"]
diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py
index e57db7471c..d119b0931b 100755
--- a/tests/topotests/conftest.py
+++ b/tests/topotests/conftest.py
@@ -26,6 +26,12 @@ def pytest_addoption(parser):
only run the setup_module() to setup the topology without running any tests.
"""
parser.addoption(
+ "--asan-abort",
+ action="store_true",
+ help="Configure address sanitizer to abort process on error",
+ )
+
+ parser.addoption(
"--gdb-breakpoints",
metavar="SYMBOL[,SYMBOL...]",
help="Comma-separated list of functions to set gdb breakpoints on",
@@ -68,6 +74,12 @@ def pytest_addoption(parser):
)
parser.addoption(
+ "--strace-daemons",
+ metavar="DAEMON[,DAEMON...]",
+ help="Comma-separated list of daemons to strace, or 'all'",
+ )
+
+ parser.addoption(
"--topology-only",
action="store_true",
default=False,
@@ -167,6 +179,9 @@ def pytest_configure(config):
if not diagnose_env():
pytest.exit("environment has errors, please read the logs")
+ asan_abort = config.getoption("--asan-abort")
+ topotest_extra_config["asan_abort"] = asan_abort
+
gdb_routers = config.getoption("--gdb-routers")
gdb_routers = gdb_routers.split(",") if gdb_routers else []
topotest_extra_config["gdb_routers"] = gdb_routers
@@ -185,6 +200,9 @@ def pytest_configure(config):
shell = config.getoption("--shell")
topotest_extra_config["shell"] = shell.split(",") if shell else []
+ strace = config.getoption("--strace-daemons")
+ topotest_extra_config["strace_daemons"] = strace.split(",") if strace else []
+
pause_after = config.getoption("--pause-after")
shell_on_error = config.getoption("--shell-on-error")
@@ -244,6 +262,11 @@ def pytest_runtest_makereport(item, call):
)
)
+ # We want to pause, if requested, on any error not just test cases
+ # (e.g., call.when == "setup")
+ if not pause:
+ pause = topotest_extra_config["pause_after"]
+
# (topogen) Set topology error to avoid advancing in the test.
tgen = get_topogen()
if tgen is not None:
diff --git a/tests/topotests/docker/frr-topotests.sh b/tests/topotests/docker/frr-topotests.sh
index 9ef59b3bbc..1eaaea2971 100755
--- a/tests/topotests/docker/frr-topotests.sh
+++ b/tests/topotests/docker/frr-topotests.sh
@@ -145,7 +145,15 @@ if [ "${TOPOTEST_PULL:-1}" = "1" ]; then
docker pull frrouting/topotests:latest
fi
+if [[ -n "$TMUX" ]]; then
+ TMUX_OPTIONS="-v $(dirname $TMUX):$(dirname $TMUX) -e TMUX=$TMUX -e TMUX_PANE=$TMUX_PANE"
+fi
+
+if [[ -n "$STY" ]]; then
+ SCREEN_OPTIONS="-v /run/screen:/run/screen -e STY=$STY"
+fi
set -- --rm -i \
+ -v "$HOME:$HOME:ro" \
-v "$TOPOTEST_LOGS:/tmp" \
-v "$TOPOTEST_FRR:/root/host-frr:ro" \
-v "$TOPOTEST_BUILDCACHE:/root/persist" \
@@ -154,6 +162,8 @@ set -- --rm -i \
-e "TOPOTEST_DOC=$TOPOTEST_DOC" \
-e "TOPOTEST_SANITIZER=$TOPOTEST_SANITIZER" \
--privileged \
+ $SCREEN_OPTINS \
+ $TMUX_OPTIONS \
$TOPOTEST_OPTIONS \
frrouting/topotests:latest "$@"
diff --git a/tests/topotests/evpn_pim_1/leaf1/pimd.conf b/tests/topotests/evpn_pim_1/leaf1/pimd.conf
index 293e252086..d85f33d1fc 100644
--- a/tests/topotests/evpn_pim_1/leaf1/pimd.conf
+++ b/tests/topotests/evpn_pim_1/leaf1/pimd.conf
@@ -2,6 +2,7 @@ debug pim events
debug pim nht
debug pim zebra
ip pim rp 192.168.100.1
+ip pim join-prune-interval 5
!
int lo
ip pim
diff --git a/tests/topotests/evpn_pim_1/leaf2/pimd.conf b/tests/topotests/evpn_pim_1/leaf2/pimd.conf
index 08d5a19a2a..d775b800b3 100644
--- a/tests/topotests/evpn_pim_1/leaf2/pimd.conf
+++ b/tests/topotests/evpn_pim_1/leaf2/pimd.conf
@@ -1,4 +1,5 @@
ip pim rp 192.168.100.1
+ip pim join-prune-interval 5
!
int lo
ip pim
diff --git a/tests/topotests/evpn_pim_1/spine/pimd.conf b/tests/topotests/evpn_pim_1/spine/pimd.conf
index 56adda5cc4..12c6d6f85c 100644
--- a/tests/topotests/evpn_pim_1/spine/pimd.conf
+++ b/tests/topotests/evpn_pim_1/spine/pimd.conf
@@ -1,4 +1,5 @@
ip pim rp 192.168.100.1
+ip pim join-prune-interval 5
!
int lo
ip pim
diff --git a/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json b/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json
index 14842da326..dd412708bb 100644
--- a/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json
+++ b/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json
@@ -41,7 +41,10 @@
"neighbor": {
"e1": {
"dest_link": {
- "r1": {}
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -55,7 +58,10 @@
"neighbor": {
"e1": {
"dest_link": {
- "r1": {}
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -106,7 +112,10 @@
"neighbor": {
"e1": {
"dest_link": {
- "r2-link1": {}
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -120,7 +129,10 @@
"neighbor": {
"e1": {
"dest_link": {
- "r2-link1": {}
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -140,7 +152,10 @@
"neighbor": {
"e1": {
"dest_link": {
- "r2-link2": {}
+ "r2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -154,7 +169,10 @@
"neighbor": {
"e1": {
"dest_link": {
- "r2-link2": {}
+ "r2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -222,7 +240,10 @@
"neighbor": {
"r1": {
"dest_link": {
- "e1": {}
+ "e1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -233,7 +254,10 @@
"neighbor": {
"r1": {
"dest_link": {
- "e1": {}
+ "e1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -262,7 +286,10 @@
"neighbor": {
"r2": {
"dest_link": {
- "e1-link1": {}
+ "e1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -273,7 +300,10 @@
"neighbor": {
"r2": {
"dest_link": {
- "e1-link1": {}
+ "e1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -302,7 +332,10 @@
"neighbor": {
"r2": {
"dest_link": {
- "e1-link2": {}
+ "e1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -313,7 +346,10 @@
"neighbor": {
"r2": {
"dest_link": {
- "e1-link2": {}
+ "e1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -342,6 +378,8 @@
"d1": {
"dest_link": {
"e1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
"deactivate": "ipv4"
}
}
@@ -349,6 +387,8 @@
"d2": {
"dest_link": {
"e1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
"deactivate": "ipv4"
}
}
@@ -412,6 +452,8 @@
"e1": {
"dest_link": {
"d1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
"deactivate": "ipv4"
}
}
@@ -442,7 +484,10 @@
"neighbor": {
"r3": {
"dest_link": {
- "d1": {}
+ "d1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -453,7 +498,10 @@
"neighbor": {
"r3": {
"dest_link": {
- "d1": {}
+ "d1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -482,7 +530,10 @@
"neighbor": {
"r4": {
"dest_link": {
- "d1-link1": {}
+ "d1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -493,7 +544,10 @@
"neighbor": {
"r4": {
"dest_link": {
- "d1-link1": {}
+ "d1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -522,7 +576,10 @@
"neighbor": {
"r4": {
"dest_link": {
- "d1-link2": {}
+ "d1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -533,7 +590,10 @@
"neighbor": {
"r4": {
"dest_link": {
- "d1-link2": {}
+ "d1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -590,7 +650,9 @@
"e1": {
"dest_link": {
"d2-link1": {
- "deactivate": "ipv4"
+ "deactivate": "ipv4",
+ "keepalivetimer": 1,
+ "holddowntimer": 3
}
}
}
@@ -620,7 +682,10 @@
"neighbor": {
"r3": {
"dest_link": {
- "d2": {}
+ "d2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -631,7 +696,10 @@
"neighbor": {
"r3": {
"dest_link": {
- "d2": {}
+ "d2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -660,7 +728,10 @@
"neighbor": {
"r4": {
"dest_link": {
- "d2-link1": {}
+ "d2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -671,7 +742,10 @@
"neighbor": {
"r4": {
"dest_link": {
- "d2-link1": {}
+ "d2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -700,7 +774,10 @@
"neighbor": {
"r4": {
"dest_link": {
- "d2-link2": {}
+ "d2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -711,7 +788,10 @@
"neighbor": {
"r4": {
"dest_link": {
- "d2-link2": {}
+ "d2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -755,12 +835,18 @@
"neighbor": {
"d1": {
"dest_link": {
- "r3": {}
+ "r3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
},
"d2": {
"dest_link": {
- "r3": {}
+ "r3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -771,12 +857,18 @@
"neighbor": {
"d1": {
"dest_link": {
- "r3": {}
+ "r3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
},
"d2": {
"dest_link": {
- "r3": {}
+ "r3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -814,12 +906,18 @@
"neighbor": {
"d1": {
"dest_link": {
- "r4-link1": {}
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
},
"d2": {
"dest_link": {
- "r4-link1": {}
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -830,12 +928,18 @@
"neighbor": {
"d1": {
"dest_link": {
- "r4-link1": {}
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
},
"d2": {
"dest_link": {
- "r4-link1": {}
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -852,12 +956,18 @@
"neighbor": {
"d1": {
"dest_link": {
- "r4-link2": {}
+ "r4-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
},
"d2": {
"dest_link": {
- "r4-link2": {}
+ "r4-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -868,12 +978,18 @@
"neighbor": {
"d1": {
"dest_link": {
- "r4-link2": {}
+ "r4-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
},
"d2": {
"dest_link": {
- "r4-link2": {}
+ "r4-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -885,3 +1001,4 @@
}
}
}
+
diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py
index a236a916b5..2f1f67439f 100644
--- a/tests/topotests/lib/bgp.py
+++ b/tests/topotests/lib/bgp.py
@@ -44,6 +44,7 @@ from lib.common_config import (
FRRCFG_FILE,
retry,
get_ipv6_linklocal_address,
+ get_frr_ipv6_linklocal
)
LOGDIR = "/tmp/topotests/"
@@ -265,6 +266,11 @@ def __create_bgp_global(tgen, input_dict, router, build=False):
config_data.append("bgp router-id {}".format(router_id))
config_data.append("no bgp network import-check")
+ bgp_peer_grp_data = bgp_data.setdefault("peer-group", {})
+
+ if "peer-group" in bgp_data and bgp_peer_grp_data:
+ peer_grp_data = __create_bgp_peer_group(tgen, bgp_peer_grp_data, router)
+ config_data.extend(peer_grp_data)
bst_path = bgp_data.setdefault("bestpath", None)
if bst_path:
@@ -380,6 +386,7 @@ def __create_bgp_unicast_neighbor(
addr_data = addr_dict["unicast"]
if addr_data:
config_data.append("address-family {} unicast".format(addr_type))
+
advertise_network = addr_data.setdefault("advertise_networks", [])
for advertise_network_dict in advertise_network:
network = advertise_network_dict["network"]
@@ -404,14 +411,29 @@ def __create_bgp_unicast_neighbor(
config_data.append(cmd)
+ import_cmd = addr_data.setdefault("import", {})
+ if import_cmd:
+ try:
+ if import_cmd["delete"]:
+ config_data.append("no import vrf {}".format(import_cmd["vrf"]))
+ except KeyError:
+ config_data.append("import vrf {}".format(import_cmd["vrf"]))
+
max_paths = addr_data.setdefault("maximum_paths", {})
if max_paths:
ibgp = max_paths.setdefault("ibgp", None)
ebgp = max_paths.setdefault("ebgp", None)
+ del_cmd = max_paths.setdefault("delete", False)
if ibgp:
- config_data.append("maximum-paths ibgp {}".format(ibgp))
+ if del_cmd:
+ config_data.append("no maximum-paths ibgp {}".format(ibgp))
+ else:
+ config_data.append("maximum-paths ibgp {}".format(ibgp))
if ebgp:
- config_data.append("maximum-paths {}".format(ebgp))
+ if del_cmd:
+ config_data.append("no maximum-paths {}".format(ebgp))
+ else:
+ config_data.append("maximum-paths {}".format(ebgp))
aggregate_addresses = addr_data.setdefault("aggregate_address", [])
for aggregate_address in aggregate_addresses:
@@ -649,6 +671,38 @@ def __create_l2vpn_evpn_address_family(
return config_data
+def __create_bgp_peer_group(topo, input_dict, router):
+ """
+ Helper API to create neighbor specific configuration
+
+ Parameters
+ ----------
+ * `topo` : json file data
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `router` : router id to be configured
+ """
+ config_data = []
+ logger.debug("Entering lib API: __create_bgp_peer_group()")
+
+ for grp, grp_dict in input_dict.items():
+ config_data.append("neighbor {} peer-group".format(grp))
+ neigh_cxt = "neighbor {} ".format(grp)
+ update_source = grp_dict.setdefault("update-source", None)
+ remote_as = grp_dict.setdefault("remote-as", None)
+ capability = grp_dict.setdefault("capability", None)
+ if update_source:
+ config_data.append("{} update-source {}".format(neigh_cxt, update_source))
+
+ if remote_as:
+ config_data.append("{} remote-as {}".format(neigh_cxt, remote_as))
+
+ if capability:
+ config_data.append("{} capability {}".format(neigh_cxt, capability))
+
+ logger.debug("Exiting lib API: __create_bgp_peer_group()")
+ return config_data
+
+
def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True):
"""
Helper API to create neighbor specific configuration
@@ -660,10 +714,9 @@ def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True):
* `input_dict` : Input dict data, required when configuring from testcase
* `router` : router id to be configured
"""
-
config_data = []
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
-
+ tgen = get_topogen()
bgp_data = input_dict["address_family"]
neigh_data = bgp_data[addr_type]["unicast"]["neighbor"]
@@ -672,35 +725,91 @@ def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True):
nh_details = topo[name]
if "vrfs" in topo[router] or type(nh_details["bgp"]) is list:
- remote_as = nh_details["bgp"][0]["local_as"]
+ for vrf_data in nh_details["bgp"]:
+ if "vrf" in nh_details["links"][dest_link] and "vrf" in vrf_data:
+ if nh_details["links"][dest_link]["vrf"] == vrf_data["vrf"]:
+ remote_as = vrf_data["local_as"]
+ break
+ else:
+ if "vrf" not in vrf_data:
+ remote_as = vrf_data["local_as"]
+ break
+
else:
remote_as = nh_details["bgp"]["local_as"]
update_source = None
- if dest_link in nh_details["links"].keys():
- ip_addr = nh_details["links"][dest_link][addr_type].split("/")[0]
- # Loopback interface
- if "source_link" in peer and peer["source_link"] == "lo":
- update_source = topo[router]["links"]["lo"][addr_type].split("/")[0]
+ if "neighbor_type" in peer and peer["neighbor_type"] == "unnumbered":
+ ip_addr = nh_details["links"][dest_link]["peer-interface"]
+ elif "neighbor_type" in peer and peer["neighbor_type"] == "link-local":
+ intf = topo[name]["links"][dest_link]["interface"]
+ ip_addr = get_frr_ipv6_linklocal(tgen, name, intf)
+ elif dest_link in nh_details["links"].keys():
+ try:
+ ip_addr = nh_details["links"][dest_link][addr_type].split("/")[0]
+ except KeyError:
+ intf = topo[name]["links"][dest_link]["interface"]
+ ip_addr = get_frr_ipv6_linklocal(tgen, name, intf)
+ if "delete" in peer and peer["delete"]:
+ neigh_cxt = "no neighbor {}".format(ip_addr)
+ config_data.append("{}".format(neigh_cxt))
+ return config_data
+ else:
+ neigh_cxt = "neighbor {}".format(ip_addr)
- neigh_cxt = "neighbor {}".format(ip_addr)
+ if "peer-group" in peer:
+ config_data.append(
+ "neighbor {} interface peer-group {}".format(
+ ip_addr, peer["peer-group"]
+ )
+ )
+
+ # Loopback interface
+ if "source_link" in peer:
+ if peer["source_link"] == "lo":
+ update_source = topo[router]["links"]["lo"][addr_type].split("/")[0]
+ else:
+ update_source = topo[router]["links"][peer["source_link"]][
+ "interface"
+ ]
+ if "peer-group" not in peer:
+ if "neighbor_type" in peer and peer["neighbor_type"] == "unnumbered":
+ config_data.append(
+ "{} interface remote-as {}".format(neigh_cxt, remote_as)
+ )
+ elif add_neigh:
+ config_data.append("{} remote-as {}".format(neigh_cxt, remote_as))
- if add_neigh:
- config_data.append("{} remote-as {}".format(neigh_cxt, remote_as))
if addr_type == "ipv6":
config_data.append("address-family ipv6 unicast")
config_data.append("{} activate".format(neigh_cxt))
+ if "neighbor_type" in peer and peer["neighbor_type"] == "link-local":
+ config_data.append(
+ "{} update-source {}".format(
+ neigh_cxt, nh_details["links"][dest_link]["peer-interface"]
+ )
+ )
+ config_data.append(
+ "{} interface {}".format(
+ neigh_cxt, nh_details["links"][dest_link]["peer-interface"]
+ )
+ )
+
disable_connected = peer.setdefault("disable_connected_check", False)
keep_alive = peer.setdefault("keepalivetimer", 3)
hold_down = peer.setdefault("holddowntimer", 10)
password = peer.setdefault("password", None)
no_password = peer.setdefault("no_password", None)
+ capability = peer.setdefault("capability", None)
max_hop_limit = peer.setdefault("ebgp_multihop", 1)
+
graceful_restart = peer.setdefault("graceful-restart", None)
graceful_restart_helper = peer.setdefault("graceful-restart-helper", None)
graceful_restart_disable = peer.setdefault("graceful-restart-disable", None)
+ if capability:
+ config_data.append("{} capability {}".format(neigh_cxt, capability))
if update_source:
config_data.append(
@@ -718,7 +827,6 @@ def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True):
config_data.append(
"{} timers {} {}".format(neigh_cxt, keep_alive, hold_down)
)
-
if graceful_restart:
config_data.append("{} graceful-restart".format(neigh_cxt))
elif graceful_restart == False:
@@ -768,7 +876,7 @@ def __create_bgp_unicast_address_family(
config_data = []
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
-
+ tgen = get_topogen()
bgp_data = input_dict["address_family"]
neigh_data = bgp_data[addr_type]["unicast"]["neighbor"]
@@ -784,16 +892,34 @@ def __create_bgp_unicast_address_family(
for destRouterLink, data in sorted(nh_details["links"].items()):
if "type" in data and data["type"] == "loopback":
if dest_link == destRouterLink:
- ip_addr = nh_details["links"][destRouterLink][
- addr_type
- ].split("/")[0]
+ ip_addr = (
+ nh_details["links"][destRouterLink][addr_type]
+ .split("/")[0]
+ .lower()
+ )
# Physical interface
else:
- if dest_link in nh_details["links"].keys():
-
- ip_addr = nh_details["links"][dest_link][addr_type].split("/")[0]
- if addr_type == "ipv4" and bgp_data["ipv6"]:
+ # check the neighbor type if un numbered nbr, use interface.
+ if "neighbor_type" in peer and peer["neighbor_type"] == "unnumbered":
+ ip_addr = nh_details["links"][dest_link]["peer-interface"]
+ elif "neighbor_type" in peer and peer["neighbor_type"] == "link-local":
+ intf = topo[peer_name]["links"][dest_link]["interface"]
+ ip_addr = get_frr_ipv6_linklocal(tgen, peer_name, intf)
+ elif dest_link in nh_details["links"].keys():
+ try:
+ ip_addr = nh_details["links"][dest_link][addr_type].split("/")[
+ 0
+ ]
+ except KeyError:
+ intf = topo[peer_name]["links"][dest_link]["interface"]
+ ip_addr = get_frr_ipv6_linklocal(tgen, peer_name, intf)
+ if (
+ addr_type == "ipv4"
+ and bgp_data["ipv6"]
+ and check_address_types("ipv6")
+ and "ipv6" in nh_details["links"][dest_link]
+ ):
deactivate = nh_details["links"][dest_link]["ipv6"].split("/")[
0
]
@@ -822,6 +948,7 @@ def __create_bgp_unicast_address_family(
prefix_lists = peer.setdefault("prefix_lists", {})
route_maps = peer.setdefault("route_maps", {})
no_send_community = peer.setdefault("no_send_community", None)
+ capability = peer.setdefault("capability", None)
allowas_in = peer.setdefault("allowas-in", None)
# next-hop-self
@@ -841,6 +968,11 @@ def __create_bgp_unicast_address_family(
"no {} send-community {}".format(neigh_cxt, no_send_community)
)
+ # capability_ext_nh
+ if capability and addr_type == "ipv6":
+ config_data.append("address-family ipv4 unicast")
+ config_data.append("{} activate".format(neigh_cxt))
+
if "allowas_in" in peer:
allow_as_in = peer["allowas_in"]
config_data.append("{} allowas-in {}".format(neigh_cxt, allow_as_in))
@@ -1067,33 +1199,37 @@ def verify_bgp_convergence(tgen, topo, dut=None, expected=True):
API will verify if BGP is converged with in the given time frame.
Running "show bgp summary json" command and verify bgp neighbor
state is established,
+
Parameters
----------
* `tgen`: topogen object
* `topo`: input json file data
* `dut`: device under test
- * `expected` : expected results from API, by-default True
Usage
-----
# To veriry is BGP is converged for all the routers used in
topology
results = verify_bgp_convergence(tgen, topo, dut="r1")
+
Returns
-------
errormsg(str) or True
"""
- logger.debug("Entering lib API: verify_bgp_convergence()")
+ result = False
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ tgen = get_topogen()
for router, rnode in tgen.routers().items():
- if dut is not None and dut != router:
+ if 'bgp' not in topo['routers'][router]:
continue
- if "bgp" not in topo["routers"][router]:
+ if dut is not None and dut != router:
continue
logger.info("Verifying BGP Convergence on router %s:", router)
- show_bgp_json = run_frr_cmd(rnode, "show bgp vrf all summary json", isjson=True)
+ show_bgp_json = run_frr_cmd(rnode, "show bgp vrf all summary json",
+ isjson=True)
# Verifying output dictionary show_bgp_json is empty or not
if not bool(show_bgp_json):
errormsg = "BGP is not running"
@@ -1115,100 +1251,6 @@ def verify_bgp_convergence(tgen, topo, dut=None, expected=True):
# To find neighbor ip type
bgp_addr_type = bgp_data["address_family"]
- if "ipv4" in bgp_addr_type or "ipv6" in bgp_addr_type:
- for addr_type in bgp_addr_type.keys():
- if not check_address_types(addr_type):
- continue
- total_peer = 0
-
- bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
-
- for bgp_neighbor in bgp_neighbors:
- total_peer += len(bgp_neighbors[bgp_neighbor]["dest_link"])
-
- for addr_type in bgp_addr_type.keys():
- if not check_address_types(addr_type):
- continue
- bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
-
- no_of_peer = 0
- for bgp_neighbor, peer_data in bgp_neighbors.items():
- for dest_link in peer_data["dest_link"].keys():
- data = topo["routers"][bgp_neighbor]["links"]
- if dest_link in data:
- peer_details = peer_data["dest_link"][dest_link]
- # for link local neighbors
- if (
- "neighbor_type" in peer_details
- and peer_details["neighbor_type"] == "link-local"
- ):
- neighbor_ip = get_ipv6_linklocal_address(
- topo["routers"], bgp_neighbor, dest_link
- )
- elif "source_link" in peer_details:
- neighbor_ip = topo["routers"][bgp_neighbor][
- "links"
- ][peer_details["source_link"]][addr_type].split(
- "/"
- )[
- 0
- ]
- elif (
- "neighbor_type" in peer_details
- and peer_details["neighbor_type"] == "unnumbered"
- ):
- neighbor_ip = data[dest_link]["peer-interface"]
- else:
- neighbor_ip = data[dest_link][addr_type].split("/")[
- 0
- ]
- nh_state = None
-
- if addr_type == "ipv4":
- if "ipv4Unicast" in show_bgp_json[vrf]:
- ipv4_data = show_bgp_json[vrf]["ipv4Unicast"][
- "peers"
- ]
- nh_state = ipv4_data[neighbor_ip]["state"]
- else:
- if "ipv6Unicast" in show_bgp_json[vrf]:
- ipv6_data = show_bgp_json[vrf]["ipv6Unicast"][
- "peers"
- ]
- nh_state = ipv6_data[neighbor_ip]["state"]
- if nh_state == "Established":
- no_of_peer += 1
-
- if "l2vpn" in bgp_addr_type:
- if "neighbor" not in bgp_addr_type["l2vpn"]["evpn"]:
- if no_of_peer == total_peer:
- logger.info(
- "[DUT: %s] VRF: %s, BGP is Converged for %s address-family",
- router,
- vrf,
- addr_type,
- )
- else:
- errormsg = (
- "[DUT: %s] VRF: %s, BGP is not converged for %s address-family"
- % (router, vrf, addr_type)
- )
- return errormsg
- else:
- if no_of_peer == total_peer:
- logger.info(
- "[DUT: %s] VRF: %s, BGP is Converged for %s address-family",
- router,
- vrf,
- addr_type,
- )
- else:
- errormsg = (
- "[DUT: %s] VRF: %s, BGP is not converged for %s address-family"
- % (router, vrf, addr_type)
- )
- return errormsg
-
if "l2vpn" in bgp_addr_type:
total_evpn_peer = 0
@@ -1224,46 +1266,120 @@ def verify_bgp_convergence(tgen, topo, dut=None, expected=True):
data = topo["routers"][bgp_neighbor]["links"]
for dest_link in dest_link_dict.keys():
if dest_link in data:
- peer_details = peer_data[_addr_type][dest_link]
+ peer_details = \
+ peer_data[_addr_type][dest_link]
- neighbor_ip = data[dest_link][_addr_type].split("/")[0]
+ neighbor_ip = \
+ data[dest_link][_addr_type].split(
+ "/")[0]
nh_state = None
- if (
- "ipv4Unicast" in show_bgp_json[vrf]
- or "ipv6Unicast" in show_bgp_json[vrf]
- ):
- errormsg = (
- "[DUT: %s] VRF: %s, "
- "ipv4Unicast/ipv6Unicast"
- " address-family present"
- " under l2vpn" % (router, vrf)
- )
+ if "ipv4Unicast" in show_bgp_json[vrf] or \
+ "ipv6Unicast" in show_bgp_json[vrf]:
+ errormsg = ("[DUT: %s] VRF: %s, "
+ "ipv4Unicast/ipv6Unicast"
+ " address-family present"
+ " under l2vpn" % (router,
+ vrf))
return errormsg
- l2VpnEvpn_data = show_bgp_json[vrf]["l2VpnEvpn"][
- "peers"
- ]
- nh_state = l2VpnEvpn_data[neighbor_ip]["state"]
+ l2VpnEvpn_data = \
+ show_bgp_json[vrf]["l2VpnEvpn"][
+ "peers"]
+ nh_state = \
+ l2VpnEvpn_data[neighbor_ip]["state"]
if nh_state == "Established":
no_of_evpn_peer += 1
if no_of_evpn_peer == total_evpn_peer:
- logger.info(
- "[DUT: %s] VRF: %s, BGP is Converged for " "epvn peers",
- router,
- vrf,
- )
+ logger.info("[DUT: %s] VRF: %s, BGP is Converged for "
+ "epvn peers", router, vrf)
+ result = True
else:
- errormsg = (
- "[DUT: %s] VRF: %s, BGP is not converged "
- "for evpn peers" % (router, vrf)
- )
+ errormsg = ("[DUT: %s] VRF: %s, BGP is not converged "
+ "for evpn peers" % (router, vrf))
return errormsg
+ else:
+ total_peer = 0
+ for addr_type in bgp_addr_type.keys():
+ if not check_address_types(addr_type):
+ continue
- logger.debug("Exiting API: verify_bgp_convergence()")
- return True
+ bgp_neighbors = \
+ bgp_addr_type[addr_type]["unicast"]["neighbor"]
+
+ for bgp_neighbor in bgp_neighbors:
+ total_peer += \
+ len(bgp_neighbors[bgp_neighbor]["dest_link"])
+
+ no_of_peer = 0
+ for addr_type in bgp_addr_type.keys():
+ if not check_address_types(addr_type):
+ continue
+ bgp_neighbors = \
+ bgp_addr_type[addr_type]["unicast"]["neighbor"]
+
+ for bgp_neighbor, peer_data in bgp_neighbors.items():
+ for dest_link in peer_data["dest_link"].\
+ keys():
+ data = \
+ topo["routers"][bgp_neighbor]["links"]
+ if dest_link in data:
+ peer_details = \
+ peer_data['dest_link'][dest_link]
+ # for link local neighbors
+ if "neighbor_type" in peer_details and \
+ peer_details["neighbor_type"] == \
+ 'link-local':
+ intf = topo["routers"][bgp_neighbor][
+ "links"][dest_link]["interface"]
+ neighbor_ip = get_frr_ipv6_linklocal(
+ tgen, bgp_neighbor, intf)
+ elif "source_link" in peer_details:
+ neighbor_ip = \
+ topo["routers"][bgp_neighbor][
+ "links"][peer_details[
+ 'source_link']][
+ addr_type].\
+ split("/")[0]
+ elif "neighbor_type" in peer_details and \
+ peer_details["neighbor_type"] == \
+ 'unnumbered':
+ neighbor_ip = \
+ data[dest_link]["peer-interface"]
+ else:
+ neighbor_ip = \
+ data[dest_link][addr_type].split(
+ "/")[0]
+ nh_state = None
+ neighbor_ip = neighbor_ip.lower()
+ if addr_type == "ipv4":
+ ipv4_data = show_bgp_json[vrf][
+ "ipv4Unicast"]["peers"]
+ nh_state = \
+ ipv4_data[neighbor_ip]["state"]
+ else:
+ ipv6_data = show_bgp_json[vrf][
+ "ipv6Unicast"]["peers"]
+ if neighbor_ip in ipv6_data:
+ nh_state = \
+ ipv6_data[neighbor_ip]["state"]
+
+ if nh_state == "Established":
+ no_of_peer += 1
+
+ if no_of_peer == total_peer and no_of_peer > 0:
+ logger.info("[DUT: %s] VRF: %s, BGP is Converged",
+ router, vrf)
+ result = True
+ else:
+ errormsg = ("[DUT: %s] VRF: %s, BGP is not converged"
+ % (router, vrf))
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
@retry(retry_timeout=16)
diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py
index d659b8d52d..07bb5153ab 100644
--- a/tests/topotests/lib/common_config.py
+++ b/tests/topotests/lib/common_config.py
@@ -365,7 +365,7 @@ def create_common_configuration(
return True
-def kill_router_daemons(tgen, router, daemons):
+def kill_router_daemons(tgen, router, daemons, save_config=True):
"""
Router's current config would be saved to /etc/frr/ for each daemon
and daemon would be killed forcefully using SIGKILL.
@@ -379,9 +379,10 @@ def kill_router_daemons(tgen, router, daemons):
try:
router_list = tgen.routers()
- # Saving router config to /etc/frr, which will be loaded to router
- # when it starts
- router_list[router].vtysh_cmd("write memory")
+ if save_config:
+ # Saving router config to /etc/frr, which will be loaded to router
+ # when it starts
+ router_list[router].vtysh_cmd("write memory")
# Kill Daemons
result = router_list[router].killDaemons(daemons)
@@ -496,7 +497,7 @@ def reset_config_on_routers(tgen, routerName=None):
f.close()
run_cfg_file = "{}/{}/frr.sav".format(TMPDIR, rname)
init_cfg_file = "{}/{}/frr_json_initial.conf".format(TMPDIR, rname)
- command = "/usr/lib/frr/frr-reload.py --input {} --test {} > {}".format(
+ command = "/usr/lib/frr/frr-reload.py --test --test-reset --input {} {} > {}".format(
run_cfg_file, init_cfg_file, dname
)
result = call(command, shell=True, stderr=SUB_STDOUT, stdout=SUB_PIPE)
@@ -526,37 +527,9 @@ def reset_config_on_routers(tgen, routerName=None):
raise InvalidCLIError(out_data)
raise InvalidCLIError("Unknown error in %s", output)
- f = open(dname, "r")
delta = StringIO()
- delta.write("configure terminal\n")
- t_delta = f.read()
-
- # Don't disable debugs
- check_debug = True
-
- for line in t_delta.split("\n"):
- line = line.strip()
- if line == "Lines To Delete" or line == "===============" or not line:
- continue
-
- if line == "Lines To Add":
- check_debug = False
- continue
-
- if line == "============" or not line:
- continue
-
- # Leave debugs and log output alone
- if check_debug:
- if "debug" in line or "log file" in line:
- continue
-
- delta.write(line)
- delta.write("\n")
-
- f.close()
-
- delta.write("end\n")
+ with open(dname, "r") as f:
+ delta.write(f.read())
output = router.vtysh_multicmd(delta.getvalue(), pretty_output=False)
@@ -636,6 +609,7 @@ def load_config_to_router(tgen, routerName, save_bkup=False):
return True
+
def get_frr_ipv6_linklocal(tgen, router, intf=None, vrf=None):
"""
API to get the link local ipv6 address of a particular interface using
@@ -668,38 +642,48 @@ def get_frr_ipv6_linklocal(tgen, router, intf=None, vrf=None):
else:
cmd = "show interface"
- ifaces = router_list[router].run('vtysh -c "{}"'.format(cmd))
-
- # Fix newlines (make them all the same)
- ifaces = ("\n".join(ifaces.splitlines()) + "\n").splitlines()
-
- interface = None
- ll_per_if_count = 0
- for line in ifaces:
- # Interface name
- m = re_search("Interface ([a-zA-Z0-9-]+) is", line)
- if m:
- interface = m.group(1).split(" ")[0]
- ll_per_if_count = 0
-
- # Interface ip
- m1 = re_search("inet6 (fe80[:a-fA-F0-9]+[/0-9]+)", line)
- if m1:
- local = m1.group(1)
- ll_per_if_count += 1
- if ll_per_if_count > 1:
- linklocal += [["%s-%s" % (interface, ll_per_if_count), local]]
- else:
- linklocal += [[interface, local]]
-
- if linklocal:
- if intf:
- return [_linklocal[1] for _linklocal in linklocal if _linklocal[0] == intf][
- 0
- ].split("/")[0]
- return linklocal
- else:
- errormsg = "Link local ip missing on router {}"
+ linklocal = []
+ if vrf:
+ cmd = "show interface vrf {}".format(vrf)
+ else:
+ cmd = "show interface"
+ for chk_ll in range(0, 60):
+ sleep(1/4)
+ ifaces = router_list[router].run('vtysh -c "{}"'.format(cmd))
+ # Fix newlines (make them all the same)
+ ifaces = ('\n'.join(ifaces.splitlines()) + '\n').splitlines()
+
+ interface = None
+ ll_per_if_count = 0
+ for line in ifaces:
+ # Interface name
+ m = re_search('Interface ([a-zA-Z0-9-]+) is', line)
+ if m:
+ interface = m.group(1).split(" ")[0]
+ ll_per_if_count = 0
+
+ # Interface ip
+ m1 = re_search('inet6 (fe80[:a-fA-F0-9]+[\/0-9]+)',
+ line)
+ if m1:
+ local = m1.group(1)
+ ll_per_if_count += 1
+ if ll_per_if_count > 1:
+ linklocal += [["%s-%s" %
+ (interface, ll_per_if_count), local]]
+ else:
+ linklocal += [[interface, local]]
+
+ try:
+ if linklocal:
+ if intf:
+ return [_linklocal[1] for _linklocal in linklocal if _linklocal[0]==intf][0].\
+ split("/")[0]
+ return linklocal
+ except IndexError:
+ continue
+
+ errormsg = "Link local ip missing on router {}".format(router)
return errormsg
@@ -1845,6 +1829,14 @@ def create_interfaces_cfg(tgen, topo, build=False):
else:
interface_data.append("ipv6 address {}".format(intf_addr))
+ # Wait for vrf interfaces to get link local address once they are up
+ if not destRouterLink == 'lo' and 'vrf' in topo[c_router][
+ 'links'][destRouterLink]:
+ vrf = topo[c_router]['links'][destRouterLink]['vrf']
+ intf = topo[c_router]['links'][destRouterLink]['interface']
+ ll = get_frr_ipv6_linklocal(tgen, c_router, intf=intf,
+ vrf = vrf)
+
if "ipv6-link-local" in data:
intf_addr = c_data["links"][destRouterLink]["ipv6-link-local"]
@@ -1867,7 +1859,7 @@ def create_interfaces_cfg(tgen, topo, build=False):
)
if "ospf6" in data:
interface_data += _create_interfaces_ospf_cfg(
- "ospf6", c_data, data, ospf_keywords
+ "ospf6", c_data, data, ospf_keywords + ["area"]
)
result = create_common_configuration(
diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py
index dc9fe0fcca..6aa7a2c0a9 100644
--- a/tests/topotests/lib/ospf.py
+++ b/tests/topotests/lib/ospf.py
@@ -28,6 +28,7 @@ from time import sleep
from lib.topolog import logger
from lib.topotest import frr_unicode
from ipaddress import IPv6Address
+
# Import common_config to use commomnly used APIs
from lib.common_config import (
create_common_configuration,
@@ -89,8 +90,7 @@ def create_router_ospf(tgen, topo, input_dict=None, build=False, load_config=Tru
logger.debug("Router %s: 'ospf' not present in input_dict", router)
continue
- result = __create_ospf_global(
- tgen, input_dict, router, build, load_config)
+ result = __create_ospf_global(tgen, input_dict, router, build, load_config)
if result is True:
ospf_data = input_dict[router]["ospf"]
@@ -100,7 +100,8 @@ def create_router_ospf(tgen, topo, input_dict=None, build=False, load_config=Tru
continue
result = __create_ospf_global(
- tgen, input_dict, router, build, load_config, ospf='ospf6')
+ tgen, input_dict, router, build, load_config, ospf="ospf6"
+ )
if result is True:
ospf_data = input_dict[router]["ospf6"]
@@ -172,7 +173,6 @@ def __create_ospf_global(
config_data.append(cmd)
-
# router id
router_id = ospf_data.setdefault("router_id", None)
del_router_id = ospf_data.setdefault("del_router_id", False)
@@ -187,8 +187,7 @@ def __create_ospf_global(
if del_log_adj_changes:
config_data.append("no log-adjacency-changes detail")
if log_adj_changes:
- config_data.append("log-adjacency-changes {}".format(
- log_adj_changes))
+ config_data.append("log-adjacency-changes {}".format(log_adj_changes))
# aggregation timer
aggr_timer = ospf_data.setdefault("aggr_timer", None)
@@ -196,8 +195,7 @@ def __create_ospf_global(
if del_aggr_timer:
config_data.append("no aggregation timer")
if aggr_timer:
- config_data.append("aggregation timer {}".format(
- aggr_timer))
+ config_data.append("aggregation timer {}".format(aggr_timer))
# maximum path information
ecmp_data = ospf_data.setdefault("maximum-paths", {})
@@ -245,12 +243,13 @@ def __create_ospf_global(
cmd = "no {}".format(cmd)
config_data.append(cmd)
- #def route information
+ # def route information
def_rte_data = ospf_data.setdefault("default-information", {})
if def_rte_data:
if "originate" not in def_rte_data:
- logger.debug("Router %s: 'originate key' not present in "
- "input_dict", router)
+ logger.debug(
+ "Router %s: 'originate key' not present in " "input_dict", router
+ )
else:
cmd = "default-information originate"
@@ -261,12 +260,10 @@ def __create_ospf_global(
cmd = cmd + " metric {}".format(def_rte_data["metric"])
if "metric-type" in def_rte_data:
- cmd = cmd + " metric-type {}".format(def_rte_data[
- "metric-type"])
+ cmd = cmd + " metric-type {}".format(def_rte_data["metric-type"])
if "route-map" in def_rte_data:
- cmd = cmd + " route-map {}".format(def_rte_data[
- "route-map"])
+ cmd = cmd + " route-map {}".format(def_rte_data["route-map"])
del_action = def_rte_data.setdefault("delete", False)
if del_action:
@@ -288,19 +285,19 @@ def __create_ospf_global(
config_data.append(cmd)
try:
- if "area" in input_dict[router]['links'][neighbor][
- 'ospf6']:
+ if "area" in input_dict[router]["links"][neighbor]["ospf6"]:
iface = input_dict[router]["links"][neighbor]["interface"]
cmd = "interface {} area {}".format(
- iface, input_dict[router]['links'][neighbor][
- 'ospf6']['area'])
- if input_dict[router]['links'][neighbor].setdefault(
- "delete", False):
+ iface,
+ input_dict[router]["links"][neighbor]["ospf6"]["area"],
+ )
+ if input_dict[router]["links"][neighbor].setdefault(
+ "delete", False
+ ):
cmd = "no {}".format(cmd)
config_data.append(cmd)
except KeyError:
- pass
-
+ pass
# summary information
summary_data = ospf_data.setdefault("summary-address", {})
@@ -420,6 +417,7 @@ def config_ospf_interface(tgen, topo, input_dict=None, build=False, load_config=
True or False
"""
logger.debug("Enter lib config_ospf_interface")
+ result = False
if not input_dict:
input_dict = deepcopy(topo)
else:
@@ -502,7 +500,7 @@ def config_ospf_interface(tgen, topo, input_dict=None, build=False, load_config=
# interface ospf mtu
if data_ospf_mtu:
cmd = "ip ospf mtu-ignore"
- if 'del_action' in ospf_data:
+ if "del_action" in ospf_data:
cmd = "no {}".format(cmd)
config_data.append(cmd)
@@ -543,8 +541,7 @@ def clear_ospf(tgen, router, ospf=None):
version = "ip"
cmd = "clear {} ospf interface".format(version)
- logger.info(
- "Clearing ospf process on router %s.. using command '%s'", router, cmd)
+ logger.info("Clearing ospf process on router %s.. using command '%s'", router, cmd)
run_frr_cmd(rnode, cmd)
logger.debug("Exiting lib API: clear_ospf()")
@@ -774,7 +771,7 @@ def verify_ospf_neighbor(tgen, topo, dut=None, input_dict=None, lan=False, expec
################################
# Verification procs
################################
-@retry(retry_timeout=20)
+@retry(retry_timeout=50)
def verify_ospf6_neighbor(tgen, topo, dut=None, input_dict=None, lan=False):
"""
This API is to verify ospf neighborship by running
@@ -825,105 +822,133 @@ def verify_ospf6_neighbor(tgen, topo, dut=None, input_dict=None, lan=False):
if input_dict:
for router, rnode in tgen.routers().items():
- if 'ospf6' not in topo['routers'][router]:
+ if "ospf6" not in topo["routers"][router]:
continue
if dut is not None and dut != router:
continue
logger.info("Verifying OSPF neighborship on router %s:", router)
- show_ospf_json = run_frr_cmd(rnode,
- "show ipv6 ospf neighbor json", isjson=True)
+ show_ospf_json = run_frr_cmd(
+ rnode, "show ipv6 ospf neighbor json", isjson=True
+ )
# Verifying output dictionary show_ospf_json is empty or not
if not bool(show_ospf_json):
errormsg = "OSPF6 is not running"
return errormsg
ospf_data_list = input_dict[router]["ospf6"]
- ospf_nbr_list = ospf_data_list['neighbors']
+ ospf_nbr_list = ospf_data_list["neighbors"]
for ospf_nbr, nbr_data in ospf_nbr_list.items():
- data_ip = data_rid = topo['routers'][ospf_nbr]['ospf6']['router_id']
+
+ try:
+ data_ip = data_rid = topo["routers"][ospf_nbr]["ospf6"]["router_id"]
+ except KeyError:
+ data_ip = data_rid = topo["routers"][nbr_data["nbr"]]["ospf6"][
+ "router_id"
+ ]
+
if ospf_nbr in data_ip:
nbr_details = nbr_data[ospf_nbr]
elif lan:
- for switch in topo['switches']:
- if 'ospf6' in topo['switches'][switch]['links'][router]:
+ for switch in topo["switches"]:
+ if "ospf6" in topo["switches"][switch]["links"][router]:
neighbor_ip = data_ip
else:
continue
else:
- neighbor_ip = data_ip[router]['ipv6'].split("/")[0]
+ neighbor_ip = data_ip[router]["ipv6"].split("/")[0]
nh_state = None
neighbor_ip = neighbor_ip.lower()
nbr_rid = data_rid
- get_index_val = dict((d['neighborId'], dict( \
- d, index=index)) for (index, d) in enumerate( \
- show_ospf_json['neighbors']))
+ get_index_val = dict(
+ (d["neighborId"], dict(d, index=index))
+ for (index, d) in enumerate(show_ospf_json["neighbors"])
+ )
try:
- nh_state = get_index_val.get(neighbor_ip)['state']
- intf_state = get_index_val.get(neighbor_ip)['ifState']
+ nh_state = get_index_val.get(neighbor_ip)["state"]
+ intf_state = get_index_val.get(neighbor_ip)["ifState"]
except TypeError:
- errormsg = "[DUT: {}] OSPF peer {} missing,from "\
- "{} ".format(router,
- nbr_rid, ospf_nbr)
+ errormsg = "[DUT: {}] OSPF peer {} missing,from " "{} ".format(
+ router, nbr_rid, ospf_nbr
+ )
return errormsg
- nbr_state = nbr_data.setdefault("state",None)
- nbr_role = nbr_data.setdefault("role",None)
+ nbr_state = nbr_data.setdefault("state", None)
+ nbr_role = nbr_data.setdefault("role", None)
if nbr_state:
if nbr_state == nh_state:
- logger.info("[DUT: {}] OSPF6 Nbr is {}:{} State {}".format
- (router, ospf_nbr, nbr_rid, nh_state))
+ logger.info(
+ "[DUT: {}] OSPF6 Nbr is {}:{} State {}".format(
+ router, ospf_nbr, nbr_rid, nh_state
+ )
+ )
result = True
else:
- errormsg = ("[DUT: {}] OSPF6 is not Converged, neighbor"
- " state is {} , Expected state is {}".format(router,
- nh_state, nbr_state))
+ errormsg = (
+ "[DUT: {}] OSPF6 is not Converged, neighbor"
+ " state is {} , Expected state is {}".format(
+ router, nh_state, nbr_state
+ )
+ )
return errormsg
if nbr_role:
if nbr_role == intf_state:
- logger.info("[DUT: {}] OSPF6 Nbr is {}: {} Role {}".format(
- router, ospf_nbr, nbr_rid, nbr_role))
+ logger.info(
+ "[DUT: {}] OSPF6 Nbr is {}: {} Role {}".format(
+ router, ospf_nbr, nbr_rid, nbr_role
+ )
+ )
else:
- errormsg = ("[DUT: {}] OSPF6 is not Converged with rid"
- "{}, role is {}, Expected role is {}".format(router,
- nbr_rid, intf_state, nbr_role))
+ errormsg = (
+ "[DUT: {}] OSPF6 is not Converged with rid"
+ "{}, role is {}, Expected role is {}".format(
+ router, nbr_rid, intf_state, nbr_role
+ )
+ )
return errormsg
continue
else:
for router, rnode in tgen.routers().items():
- if 'ospf6' not in topo['routers'][router]:
+ if "ospf6" not in topo["routers"][router]:
continue
if dut is not None and dut != router:
continue
logger.info("Verifying OSPF6 neighborship on router %s:", router)
- show_ospf_json = run_frr_cmd(rnode,
- "show ipv6 ospf neighbor json", isjson=True)
+ show_ospf_json = run_frr_cmd(
+ rnode, "show ipv6 ospf neighbor json", isjson=True
+ )
# Verifying output dictionary show_ospf_json is empty or not
if not bool(show_ospf_json):
errormsg = "OSPF6 is not running"
return errormsg
ospf_data_list = topo["routers"][router]["ospf6"]
- ospf_neighbors = ospf_data_list['neighbors']
+ ospf_neighbors = ospf_data_list["neighbors"]
total_peer = 0
total_peer = len(ospf_neighbors.keys())
no_of_ospf_nbr = 0
- ospf_nbr_list = ospf_data_list['neighbors']
+ ospf_nbr_list = ospf_data_list["neighbors"]
no_of_peer = 0
for ospf_nbr, nbr_data in ospf_nbr_list.items():
- data_ip = data_rid = topo['routers'][ospf_nbr]['ospf6']['router_id']
+ try:
+ data_ip = data_rid = topo["routers"][ospf_nbr]["ospf6"]["router_id"]
+ except KeyError:
+ data_ip = data_rid = topo["routers"][nbr_data["nbr"]]["ospf6"][
+ "router_id"
+ ]
+
if ospf_nbr in data_ip:
nbr_details = nbr_data[ospf_nbr]
elif lan:
- for switch in topo['switches']:
- if 'ospf6' in topo['switches'][switch]['links'][router]:
+ for switch in topo["switches"]:
+ if "ospf6" in topo["switches"][switch]["links"][router]:
neighbor_ip = data_ip
else:
continue
@@ -933,26 +958,27 @@ def verify_ospf6_neighbor(tgen, topo, dut=None, input_dict=None, lan=False):
nh_state = None
neighbor_ip = neighbor_ip.lower()
nbr_rid = data_rid
- get_index_val = dict((d['neighborId'], dict( \
- d, index=index)) for (index, d) in enumerate( \
- show_ospf_json['neighbors']))
+ get_index_val = dict(
+ (d["neighborId"], dict(d, index=index))
+ for (index, d) in enumerate(show_ospf_json["neighbors"])
+ )
try:
- nh_state = get_index_val.get(neighbor_ip)['state']
- intf_state = get_index_val.get(neighbor_ip)['ifState']
+ nh_state = get_index_val.get(neighbor_ip)["state"]
+ intf_state = get_index_val.get(neighbor_ip)["ifState"]
except TypeError:
- errormsg = "[DUT: {}] OSPF peer {} missing,from "\
- "{} ".format(router,
- nbr_rid, ospf_nbr)
+ errormsg = "[DUT: {}] OSPF peer {} missing,from " "{} ".format(
+ router, nbr_rid, ospf_nbr
+ )
return errormsg
- if nh_state == 'Full':
+ if nh_state == "Full":
no_of_peer += 1
if no_of_peer == total_peer:
logger.info("[DUT: {}] OSPF6 is Converged".format(router))
result = True
else:
- errormsg = ("[DUT: {}] OSPF6 is not Converged".format(router))
+ errormsg = "[DUT: {}] OSPF6 is not Converged".format(router)
return errormsg
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
@@ -1627,31 +1653,34 @@ def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None,
found_routes = []
missing_routes = []
- if "static_routes" in input_dict[routerInput] or \
- "prefix" in input_dict[routerInput]:
+ if (
+ "static_routes" in input_dict[routerInput]
+ or "prefix" in input_dict[routerInput]
+ ):
if "prefix" in input_dict[routerInput]:
static_routes = input_dict[routerInput]["prefix"]
else:
static_routes = input_dict[routerInput]["static_routes"]
-
for static_route in static_routes:
cmd = "{}".format(command)
cmd = "{} json".format(cmd)
- ospf_rib_json = run_frr_cmd(rnode, cmd, isjson=True)
+ ospf_rib_json = run_frr_cmd(rnode, cmd, isjson=True)
# Fix for PR 2644182
try:
- ospf_rib_json = ospf_rib_json['routes']
+ ospf_rib_json = ospf_rib_json["routes"]
except KeyError:
pass
# Verifying output dictionary ospf_rib_json is not empty
if bool(ospf_rib_json) is False:
- errormsg = "[DUT: {}] No routes found in OSPF6 route " \
+ errormsg = (
+ "[DUT: {}] No routes found in OSPF6 route "
"table".format(router)
+ )
return errormsg
network = static_route["network"]
@@ -1659,7 +1688,6 @@ def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None,
_tag = static_route.setdefault("tag", None)
_rtype = static_route.setdefault("routeType", None)
-
# Generating IPs for verification
ip_list = generate_ips(network, no_of_ip)
st_found = False
@@ -1668,7 +1696,7 @@ def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None,
st_rt = str(ipaddress.ip_network(frr_unicode(st_rt)))
_addr_type = validate_ip_address(st_rt)
- if _addr_type != 'ipv6':
+ if _addr_type != "ipv6":
continue
if st_rt in ospf_rib_json:
@@ -1681,17 +1709,26 @@ def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None,
next_hop = [next_hop]
for mnh in range(0, len(ospf_rib_json[st_rt])):
- if 'fib' in ospf_rib_json[st_rt][
- mnh]["nextHops"][0]:
- found_hops.append([rib_r[
- "ip"] for rib_r in ospf_rib_json[
- st_rt][mnh]["nextHops"]])
+ if (
+ "fib"
+ in ospf_rib_json[st_rt][mnh]["nextHops"][0]
+ ):
+ found_hops.append(
+ [
+ rib_r["ip"]
+ for rib_r in ospf_rib_json[st_rt][mnh][
+ "nextHops"
+ ]
+ ]
+ )
if found_hops[0]:
- missing_list_of_nexthops = \
- set(found_hops[0]).difference(next_hop)
- additional_nexthops_in_required_nhs = \
- set(next_hop).difference(found_hops[0])
+ missing_list_of_nexthops = set(
+ found_hops[0]
+ ).difference(next_hop)
+ additional_nexthops_in_required_nhs = set(
+ next_hop
+ ).difference(found_hops[0])
if additional_nexthops_in_required_nhs:
logger.info(
@@ -1699,13 +1736,18 @@ def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None,
"%s is not active for route %s in "
"RIB of router %s\n",
additional_nexthops_in_required_nhs,
- st_rt, dut)
+ st_rt,
+ dut,
+ )
errormsg = (
"Nexthop {} is not active"
" for route {} in RIB of router"
" {}\n".format(
- additional_nexthops_in_required_nhs,
- st_rt, dut))
+ additional_nexthops_in_required_nhs,
+ st_rt,
+ dut,
+ )
+ )
return errormsg
else:
nh_found = True
@@ -1713,98 +1755,118 @@ def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None,
elif next_hop and fib is None:
if type(next_hop) is not list:
next_hop = [next_hop]
- found_hops = [rib_r['nextHop'] for rib_r in
- ospf_rib_json[st_rt][
- "nextHops"]]
+ found_hops = [
+ rib_r["nextHop"]
+ for rib_r in ospf_rib_json[st_rt]["nextHops"]
+ ]
if found_hops:
- missing_list_of_nexthops = \
- set(found_hops).difference(next_hop)
- additional_nexthops_in_required_nhs = \
- set(next_hop).difference(found_hops)
+ missing_list_of_nexthops = set(
+ found_hops
+ ).difference(next_hop)
+ additional_nexthops_in_required_nhs = set(
+ next_hop
+ ).difference(found_hops)
if additional_nexthops_in_required_nhs:
logger.info(
- "Missing nexthop %s for route"\
- " %s in RIB of router %s\n", \
- additional_nexthops_in_required_nhs, \
- st_rt, dut)
- errormsg=("Nexthop {} is Missing for "\
- "route {} in RIB of router {}\n".format(
+ "Missing nexthop %s for route"
+ " %s in RIB of router %s\n",
additional_nexthops_in_required_nhs,
- st_rt, dut))
+ st_rt,
+ dut,
+ )
+ errormsg = (
+ "Nexthop {} is Missing for "
+ "route {} in RIB of router {}\n".format(
+ additional_nexthops_in_required_nhs,
+ st_rt,
+ dut,
+ )
+ )
return errormsg
else:
nh_found = True
if _rtype:
- if "destinationType" not in ospf_rib_json[
- st_rt]:
- errormsg = ("[DUT: {}]: destinationType missing"
- "for route {} in OSPF RIB \n".\
- format(dut, st_rt))
+ if "destinationType" not in ospf_rib_json[st_rt]:
+ errormsg = (
+ "[DUT: {}]: destinationType missing"
+ "for route {} in OSPF RIB \n".format(dut, st_rt)
+ )
return errormsg
- elif _rtype != ospf_rib_json[st_rt][
- "destinationType"]:
- errormsg = ("[DUT: {}]: destinationType mismatch"
- "for route {} in OSPF RIB \n".\
- format(dut, st_rt))
+ elif _rtype != ospf_rib_json[st_rt]["destinationType"]:
+ errormsg = (
+ "[DUT: {}]: destinationType mismatch"
+ "for route {} in OSPF RIB \n".format(dut, st_rt)
+ )
return errormsg
else:
- logger.info("DUT: {}]: Found destinationType {}"
- "for route {}".\
- format(dut, _rtype, st_rt))
+ logger.info(
+ "DUT: {}]: Found destinationType {}"
+ "for route {}".format(dut, _rtype, st_rt)
+ )
if tag:
- if "tag" not in ospf_rib_json[
- st_rt]:
- errormsg = ("[DUT: {}]: tag is not"
- " present for"
- " route {} in RIB \n".\
- format(dut, st_rt
- ))
+ if "tag" not in ospf_rib_json[st_rt]:
+ errormsg = (
+ "[DUT: {}]: tag is not"
+ " present for"
+ " route {} in RIB \n".format(dut, st_rt)
+ )
return errormsg
- if _tag != ospf_rib_json[
- st_rt]["tag"]:
- errormsg = ("[DUT: {}]: tag value {}"
- " is not matched for"
- " route {} in RIB \n".\
- format(dut, _tag, st_rt,
- ))
+ if _tag != ospf_rib_json[st_rt]["tag"]:
+ errormsg = (
+ "[DUT: {}]: tag value {}"
+ " is not matched for"
+ " route {} in RIB \n".format(
+ dut,
+ _tag,
+ st_rt,
+ )
+ )
return errormsg
if metric is not None:
- if "type2cost" not in ospf_rib_json[
- st_rt]:
- errormsg = ("[DUT: {}]: metric is"
- " not present for"
- " route {} in RIB \n".\
- format(dut, st_rt))
+ if "type2cost" not in ospf_rib_json[st_rt]:
+ errormsg = (
+ "[DUT: {}]: metric is"
+ " not present for"
+ " route {} in RIB \n".format(dut, st_rt)
+ )
return errormsg
- if metric != ospf_rib_json[
- st_rt]["type2cost"]:
- errormsg = ("[DUT: {}]: metric value "
- "{} is not matched for "
- "route {} in RIB \n".\
- format(dut, metric, st_rt,
- ))
+ if metric != ospf_rib_json[st_rt]["type2cost"]:
+ errormsg = (
+ "[DUT: {}]: metric value "
+ "{} is not matched for "
+ "route {} in RIB \n".format(
+ dut,
+ metric,
+ st_rt,
+ )
+ )
return errormsg
else:
missing_routes.append(st_rt)
if nh_found:
- logger.info("[DUT: {}]: Found next_hop {} for all OSPF"
- " routes in RIB".format(router, next_hop))
+ logger.info(
+ "[DUT: {}]: Found next_hop {} for all OSPF"
+ " routes in RIB".format(router, next_hop)
+ )
if len(missing_routes) > 0:
- errormsg = ("[DUT: {}]: Missing route in RIB, "
- "routes: {}".\
- format(dut, missing_routes))
+ errormsg = "[DUT: {}]: Missing route in RIB, " "routes: {}".format(
+ dut, missing_routes
+ )
return errormsg
if found_routes:
- logger.info("[DUT: %s]: Verified routes in RIB, found"
- " routes are: %s\n", dut, found_routes)
+ logger.info(
+ "[DUT: %s]: Verified routes in RIB, found" " routes are: %s\n",
+ dut,
+ found_routes,
+ )
result = True
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
@@ -1855,15 +1917,16 @@ def verify_ospf6_interface(tgen, topo, dut=None,lan=False, input_dict=None):
result = False
for router, rnode in tgen.routers().iteritems():
- if 'ospf6' not in topo['routers'][router]:
+ if "ospf6" not in topo["routers"][router]:
continue
if dut is not None and dut != router:
continue
logger.info("Verifying OSPF interface on router %s:", router)
- show_ospf_json = run_frr_cmd(rnode, "show ipv6 ospf interface json",
- isjson=True)
+ show_ospf_json = run_frr_cmd(
+ rnode, "show ipv6 ospf interface json", isjson=True
+ )
# Verifying output dictionary show_ospf_json is empty or not
if not bool(show_ospf_json):
@@ -1873,32 +1936,49 @@ def verify_ospf6_interface(tgen, topo, dut=None,lan=False, input_dict=None):
# To find neighbor ip type
ospf_intf_data = input_dict[router]["links"]
for ospf_intf, intf_data in ospf_intf_data.items():
- intf = topo['routers'][router]['links'][ospf_intf]['interface']
- if intf in show_ospf_json:
- for intf_attribute in intf_data['ospf6']:
- if intf_data['ospf6'][intf_attribute] is not list:
- if intf_data['ospf6'][intf_attribute] == show_ospf_json[
- intf][intf_attribute]:
- logger.info("[DUT: %s] OSPF6 interface %s: %s is %s",
- router, intf, intf_attribute, intf_data['ospf6'][
- intf_attribute])
- elif intf_data['ospf6'][intf_attribute] is list:
+ intf = topo["routers"][router]["links"][ospf_intf]["interface"]
+ if intf in show_ospf_json:
+ for intf_attribute in intf_data["ospf6"]:
+ if intf_data["ospf6"][intf_attribute] is not list:
+ if (
+ intf_data["ospf6"][intf_attribute]
+ == show_ospf_json[intf][intf_attribute]
+ ):
+ logger.info(
+ "[DUT: %s] OSPF6 interface %s: %s is %s",
+ router,
+ intf,
+ intf_attribute,
+ intf_data["ospf6"][intf_attribute],
+ )
+ elif intf_data["ospf6"][intf_attribute] is list:
for addr_list in len(show_ospf_json[intf][intf_attribute]):
- if show_ospf_json[intf][intf_attribute][addr_list][
- 'address'].split('/')[0] == intf_data['ospf6'][
- 'internetAddress'][0]['address']:
- break
+ if (
+ show_ospf_json[intf][intf_attribute][addr_list][
+ "address"
+ ].split("/")[0]
+ == intf_data["ospf6"]["internetAddress"][0]["address"]
+ ):
+ break
else:
- errormsg= "[DUT: {}] OSPF6 interface {}: {} is {}, \
- Expected is {}".format(router, intf, intf_attribute,
- intf_data['ospf6'][intf_attribute], intf_data['ospf6'][
- intf_attribute])
+ errormsg = "[DUT: {}] OSPF6 interface {}: {} is {}, \
+ Expected is {}".format(
+ router,
+ intf,
+ intf_attribute,
+ intf_data["ospf6"][intf_attribute],
+ intf_data["ospf6"][intf_attribute],
+ )
return errormsg
else:
- errormsg= "[DUT: {}] OSPF6 interface {}: {} is {}, \
- Expected is {}".format(router, intf, intf_attribute,
- intf_data['ospf6'][intf_attribute], intf_data['ospf6'][
- intf_attribute])
+ errormsg = "[DUT: {}] OSPF6 interface {}: {} is {}, \
+ Expected is {}".format(
+ router,
+ intf,
+ intf_attribute,
+ intf_data["ospf6"][intf_attribute],
+ intf_data["ospf6"][intf_attribute],
+ )
return errormsg
result = True
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
@@ -1956,16 +2036,14 @@ def verify_ospf6_database(tgen, topo, dut, input_dict):
router = dut
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
- if 'ospf' not in topo['routers'][dut]:
- errormsg = "[DUT: {}] OSPF is not configured on the router.".format(
- dut)
+ if "ospf" not in topo["routers"][dut]:
+ errormsg = "[DUT: {}] OSPF is not configured on the router.".format(dut)
return errormsg
rnode = tgen.routers()[dut]
logger.info("Verifying OSPF interface on router %s:", dut)
- show_ospf_json = run_frr_cmd(rnode, "show ip ospf database json",
- isjson=True)
+ show_ospf_json = run_frr_cmd(rnode, "show ip ospf database json", isjson=True)
# Verifying output dictionary show_ospf_json is empty or not
if not bool(show_ospf_json):
errormsg = "OSPF is not running"
@@ -1973,167 +2051,209 @@ def verify_ospf6_database(tgen, topo, dut, input_dict):
# for inter and inter lsa's
ospf_db_data = input_dict.setdefault("areas", None)
- ospf_external_lsa = input_dict.setdefault(
- 'asExternalLinkStates', None)
+ ospf_external_lsa = input_dict.setdefault("asExternalLinkStates", None)
if ospf_db_data:
- for ospf_area, area_lsa in ospf_db_data.items():
- if ospf_area in show_ospf_json['areas']:
- if 'routerLinkStates' in area_lsa:
- for lsa in area_lsa['routerLinkStates']:
- for rtrlsa in show_ospf_json['areas'][ospf_area][
- 'routerLinkStates']:
- if lsa['lsaId'] == rtrlsa['lsaId'] and \
- lsa['advertisedRouter'] == rtrlsa[
- 'advertisedRouter']:
- result = True
- break
- if result:
- logger.info(
- "[DUT: %s] OSPF LSDB area %s:Router "
- "LSA %s", router, ospf_area, lsa)
+ for ospf_area, area_lsa in ospf_db_data.items():
+ if ospf_area in show_ospf_json["areas"]:
+ if "routerLinkStates" in area_lsa:
+ for lsa in area_lsa["routerLinkStates"]:
+ for rtrlsa in show_ospf_json["areas"][ospf_area][
+ "routerLinkStates"
+ ]:
+ if (
+ lsa["lsaId"] == rtrlsa["lsaId"]
+ and lsa["advertisedRouter"]
+ == rtrlsa["advertisedRouter"]
+ ):
+ result = True
break
- else:
- errormsg = \
- "[DUT: {}] OSPF LSDB area {}: expected" \
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:Router " "LSA %s",
+ router,
+ ospf_area,
+ lsa,
+ )
+ break
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB area {}: expected"
" Router LSA is {}".format(router, ospf_area, lsa)
- return errormsg
+ )
+ return errormsg
- if 'networkLinkStates' in area_lsa:
- for lsa in area_lsa['networkLinkStates']:
- for netlsa in show_ospf_json['areas'][ospf_area][
- 'networkLinkStates']:
- if lsa in show_ospf_json['areas'][ospf_area][
- 'networkLinkStates']:
- if lsa['lsaId'] == netlsa['lsaId'] and \
- lsa['advertisedRouter'] == netlsa[
- 'advertisedRouter']:
- result = True
- break
- if result:
- logger.info(
- "[DUT: %s] OSPF LSDB area %s:Network "
- "LSA %s", router, ospf_area, lsa)
- break
- else:
- errormsg = \
- "[DUT: {}] OSPF LSDB area {}: expected" \
+ if "networkLinkStates" in area_lsa:
+ for lsa in area_lsa["networkLinkStates"]:
+ for netlsa in show_ospf_json["areas"][ospf_area][
+ "networkLinkStates"
+ ]:
+ if (
+ lsa
+ in show_ospf_json["areas"][ospf_area][
+ "networkLinkStates"
+ ]
+ ):
+ if (
+ lsa["lsaId"] == netlsa["lsaId"]
+ and lsa["advertisedRouter"]
+ == netlsa["advertisedRouter"]
+ ):
+ result = True
+ break
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:Network " "LSA %s",
+ router,
+ ospf_area,
+ lsa,
+ )
+ break
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB area {}: expected"
" Network LSA is {}".format(router, ospf_area, lsa)
- return errormsg
+ )
+ return errormsg
- if 'summaryLinkStates' in area_lsa:
- for lsa in area_lsa['summaryLinkStates']:
- for t3lsa in show_ospf_json['areas'][ospf_area][
- 'summaryLinkStates']:
- if lsa['lsaId'] == t3lsa['lsaId'] and \
- lsa['advertisedRouter'] == t3lsa[
- 'advertisedRouter']:
- result = True
- break
- if result:
- logger.info(
- "[DUT: %s] OSPF LSDB area %s:Summary "
- "LSA %s", router, ospf_area, lsa)
+ if "summaryLinkStates" in area_lsa:
+ for lsa in area_lsa["summaryLinkStates"]:
+ for t3lsa in show_ospf_json["areas"][ospf_area][
+ "summaryLinkStates"
+ ]:
+ if (
+ lsa["lsaId"] == t3lsa["lsaId"]
+ and lsa["advertisedRouter"] == t3lsa["advertisedRouter"]
+ ):
+ result = True
break
- else:
- errormsg = \
- "[DUT: {}] OSPF LSDB area {}: expected" \
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:Summary " "LSA %s",
+ router,
+ ospf_area,
+ lsa,
+ )
+ break
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB area {}: expected"
" Summary LSA is {}".format(router, ospf_area, lsa)
- return errormsg
+ )
+ return errormsg
- if 'nssaExternalLinkStates' in area_lsa:
- for lsa in area_lsa['nssaExternalLinkStates']:
- for t7lsa in show_ospf_json['areas'][ospf_area][
- 'nssaExternalLinkStates']:
- if lsa['lsaId'] == t7lsa['lsaId'] and \
- lsa['advertisedRouter'] == t7lsa[
- 'advertisedRouter']:
- result = True
- break
- if result:
- logger.info(
- "[DUT: %s] OSPF LSDB area %s:Type7 "
- "LSA %s", router, ospf_area, lsa)
+ if "nssaExternalLinkStates" in area_lsa:
+ for lsa in area_lsa["nssaExternalLinkStates"]:
+ for t7lsa in show_ospf_json["areas"][ospf_area][
+ "nssaExternalLinkStates"
+ ]:
+ if (
+ lsa["lsaId"] == t7lsa["lsaId"]
+ and lsa["advertisedRouter"] == t7lsa["advertisedRouter"]
+ ):
+ result = True
break
- else:
- errormsg = \
- "[DUT: {}] OSPF LSDB area {}: expected" \
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:Type7 " "LSA %s",
+ router,
+ ospf_area,
+ lsa,
+ )
+ break
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB area {}: expected"
" Type7 LSA is {}".format(router, ospf_area, lsa)
- return errormsg
+ )
+ return errormsg
- if 'asbrSummaryLinkStates' in area_lsa:
- for lsa in area_lsa['asbrSummaryLinkStates']:
- for t4lsa in show_ospf_json['areas'][ospf_area][
- 'asbrSummaryLinkStates']:
- if lsa['lsaId'] == t4lsa['lsaId'] and \
- lsa['advertisedRouter'] == t4lsa[
- 'advertisedRouter']:
- result = True
- break
- if result:
- logger.info(
- "[DUT: %s] OSPF LSDB area %s:ASBR Summary "
- "LSA %s", router, ospf_area, lsa)
+ if "asbrSummaryLinkStates" in area_lsa:
+ for lsa in area_lsa["asbrSummaryLinkStates"]:
+ for t4lsa in show_ospf_json["areas"][ospf_area][
+ "asbrSummaryLinkStates"
+ ]:
+ if (
+ lsa["lsaId"] == t4lsa["lsaId"]
+ and lsa["advertisedRouter"] == t4lsa["advertisedRouter"]
+ ):
result = True
- else:
- errormsg = \
- "[DUT: {}] OSPF LSDB area {}: expected" \
- " ASBR Summary LSA is {}".format(
- router, ospf_area, lsa)
- return errormsg
+ break
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:ASBR Summary " "LSA %s",
+ router,
+ ospf_area,
+ lsa,
+ )
+ result = True
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB area {}: expected"
+ " ASBR Summary LSA is {}".format(router, ospf_area, lsa)
+ )
+ return errormsg
- if 'linkLocalOpaqueLsa' in area_lsa:
- for lsa in area_lsa['linkLocalOpaqueLsa']:
- try:
- for lnklsa in show_ospf_json['areas'][ospf_area][
- 'linkLocalOpaqueLsa']:
- if lsa['lsaId'] in lnklsa['lsaId'] and \
- 'linkLocalOpaqueLsa' in show_ospf_json[
- 'areas'][ospf_area]:
- logger.info((
- "[DUT: FRR] OSPF LSDB area %s:Opaque-LSA"
- "%s", ospf_area, lsa))
- result = True
- else:
- errormsg = ("[DUT: FRR] OSPF LSDB area: {} "
- "expected Opaque-LSA is {}, Found is {}".format(
- ospf_area, lsa, show_ospf_json))
- raise ValueError (errormsg)
- return errormsg
- except KeyError:
- errormsg = ("[DUT: FRR] linkLocalOpaqueLsa Not "
- "present")
- return errormsg
+ if "linkLocalOpaqueLsa" in area_lsa:
+ for lsa in area_lsa["linkLocalOpaqueLsa"]:
+ try:
+ for lnklsa in show_ospf_json["areas"][ospf_area][
+ "linkLocalOpaqueLsa"
+ ]:
+ if (
+ lsa["lsaId"] in lnklsa["lsaId"]
+ and "linkLocalOpaqueLsa"
+ in show_ospf_json["areas"][ospf_area]
+ ):
+ logger.info(
+ (
+ "[DUT: FRR] OSPF LSDB area %s:Opaque-LSA"
+ "%s",
+ ospf_area,
+ lsa,
+ )
+ )
+ result = True
+ else:
+ errormsg = (
+ "[DUT: FRR] OSPF LSDB area: {} "
+ "expected Opaque-LSA is {}, Found is {}".format(
+ ospf_area, lsa, show_ospf_json
+ )
+ )
+ raise ValueError(errormsg)
+ return errormsg
+ except KeyError:
+ errormsg = "[DUT: FRR] linkLocalOpaqueLsa Not " "present"
+ return errormsg
if ospf_external_lsa:
- for lsa in ospf_external_lsa:
- try:
- for t5lsa in show_ospf_json['asExternalLinkStates']:
- if lsa['lsaId'] == t5lsa['lsaId'] and \
- lsa['advertisedRouter'] == t5lsa[
- 'advertisedRouter']:
- result = True
- break
- except KeyError:
- result = False
- if result:
- logger.info(
- "[DUT: %s] OSPF LSDB:External LSA %s",
- router, lsa)
- result = True
- else:
- errormsg = \
- "[DUT: {}] OSPF LSDB : expected" \
- " External LSA is {}".format(router, lsa)
- return errormsg
+ for lsa in ospf_external_lsa:
+ try:
+ for t5lsa in show_ospf_json["asExternalLinkStates"]:
+ if (
+ lsa["lsaId"] == t5lsa["lsaId"]
+ and lsa["advertisedRouter"] == t5lsa["advertisedRouter"]
+ ):
+ result = True
+ break
+ except KeyError:
+ result = False
+ if result:
+ logger.info("[DUT: %s] OSPF LSDB:External LSA %s", router, lsa)
+ result = True
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB : expected"
+ " External LSA is {}".format(router, lsa)
+ )
+ return errormsg
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return result
-
-def config_ospf6_interface (tgen, topo, input_dict=None, build=False,
- load_config=True):
+def config_ospf6_interface(tgen, topo, input_dict=None, build=False, load_config=True):
"""
API to configure ospf on router.
@@ -2180,17 +2300,17 @@ def config_ospf6_interface (tgen, topo, input_dict=None, build=False,
"input_dict, passed input_dict %s", router,
str(input_dict))
continue
- ospf_data = input_dict[router]['links'][lnk]['ospf6']
+ ospf_data = input_dict[router]["links"][lnk]["ospf6"]
data_ospf_area = ospf_data.setdefault("area", None)
- data_ospf_auth = ospf_data.setdefault("authentication", None)
+ data_ospf_auth = ospf_data.setdefault("hash-algo", None)
data_ospf_dr_priority = ospf_data.setdefault("priority", None)
data_ospf_cost = ospf_data.setdefault("cost", None)
data_ospf_mtu = ospf_data.setdefault("mtu_ignore", None)
try:
- intf = topo['routers'][router]['links'][lnk]['interface']
+ intf = topo["routers"][router]["links"][lnk]["interface"]
except KeyError:
- intf = topo['switches'][router]['links'][lnk]['interface']
+ intf = topo["switches"][router]["links"][lnk]["interface"]
# interface
cmd = "interface {}".format(intf)
@@ -2201,34 +2321,50 @@ def config_ospf6_interface (tgen, topo, input_dict=None, build=False,
cmd = "ipv6 ospf area {}".format(data_ospf_area)
config_data.append(cmd)
+ # interface ospf auth
+ if data_ospf_auth:
+ cmd = "ipv6 ospf6 authentication"
+
+ if "del_action" in ospf_data:
+ cmd = "no {}".format(cmd)
+
+ if "hash-algo" in ospf_data:
+ cmd = "{} key-id {} hash-algo {} key {}".format(
+ cmd,
+ ospf_data["key-id"],
+ ospf_data["hash-algo"],
+ ospf_data["key"],
+ )
+ if "del_action" in ospf_data:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
# interface ospf dr priority
if data_ospf_dr_priority:
- cmd = "ipv6 ospf priority {}".format(
- ospf_data["priority"])
- if 'del_action' in ospf_data:
+ cmd = "ipv6 ospf priority {}".format(ospf_data["priority"])
+ if "del_action" in ospf_data:
cmd = "no {}".format(cmd)
config_data.append(cmd)
# interface ospf cost
if data_ospf_cost:
- cmd = "ipv6 ospf cost {}".format(
- ospf_data["cost"])
- if 'del_action' in ospf_data:
+ cmd = "ipv6 ospf cost {}".format(ospf_data["cost"])
+ if "del_action" in ospf_data:
cmd = "no {}".format(cmd)
config_data.append(cmd)
# interface ospf mtu
if data_ospf_mtu:
cmd = "ipv6 ospf mtu-ignore"
- if 'del_action' in ospf_data:
+ if "del_action" in ospf_data:
cmd = "no {}".format(cmd)
config_data.append(cmd)
if build:
return config_data
else:
- result = create_common_configuration(tgen, router, config_data,
- "interface_config",
- build=build)
+ result = create_common_configuration(
+ tgen, router, config_data, "interface_config", build=build
+ )
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return result
diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py
index ade5933504..b998878118 100644
--- a/tests/topotests/lib/topogen.py
+++ b/tests/topotests/lib/topogen.py
@@ -801,8 +801,8 @@ class TopoRouter(TopoGear):
try:
return json.loads(output)
- except ValueError:
- logger.warning("vtysh_cmd: failed to convert json output")
+ except ValueError as error:
+ logger.warning("vtysh_cmd: %s: failed to convert json output: %s: %s", self.name, str(output), str(error))
return {}
def vtysh_multicmd(self, commands, pretty_output=True, daemon=None):
diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py
index d1f60bfe0d..b516a67d5c 100644
--- a/tests/topotests/lib/topotest.py
+++ b/tests/topotests/lib/topotest.py
@@ -1152,6 +1152,18 @@ class Router(Node):
self.reportCores = True
self.version = None
+ self.ns_cmd = "sudo nsenter -m -n -t {} ".format(self.pid)
+ try:
+ # Allow escaping from running inside docker
+ cgroup = open("/proc/1/cgroup").read()
+ m = re.search("[0-9]+:cpuset:/docker/([a-f0-9]+)", cgroup)
+ if m:
+ self.ns_cmd = "docker exec -it {} ".format(m.group(1)) + self.ns_cmd
+ except IOError:
+ pass
+ else:
+ logger.debug("CMD to enter {}: {}".format(self.name, self.ns_cmd))
+
def _config_frr(self, **params):
"Configure FRR binaries"
self.daemondir = params.get("frrdir")
@@ -1223,25 +1235,28 @@ class Router(Node):
dmns = rundaemons.split("\n")
# Exclude empty string at end of list
for d in dmns[:-1]:
- daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip()
- if daemonpid.isdigit() and pid_exists(int(daemonpid)):
- daemonname = os.path.basename(d.rstrip().rsplit(".", 1)[0])
- logger.info("{}: stopping {}".format(self.name, daemonname))
- try:
- os.kill(int(daemonpid), signal.SIGTERM)
- except OSError as err:
- if err.errno == errno.ESRCH:
- logger.error(
- "{}: {} left a dead pidfile (pid={})".format(
- self.name, daemonname, daemonpid
+ # Only check if daemonfilepath starts with /
+ # Avoids hang on "-> Connection closed" in above self.cmd()
+ if d[0] == '/':
+ daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip()
+ if daemonpid.isdigit() and pid_exists(int(daemonpid)):
+ daemonname = os.path.basename(d.rstrip().rsplit(".", 1)[0])
+ logger.info("{}: stopping {}".format(self.name, daemonname))
+ try:
+ os.kill(int(daemonpid), signal.SIGTERM)
+ except OSError as err:
+ if err.errno == errno.ESRCH:
+ logger.error(
+ "{}: {} left a dead pidfile (pid={})".format(
+ self.name, daemonname, daemonpid
+ )
)
- )
- else:
- logger.info(
- "{}: {} could not kill pid {}: {}".format(
- self.name, daemonname, daemonpid, str(err)
+ else:
+ logger.info(
+ "{}: {} could not kill pid {}: {}".format(
+ self.name, daemonname, daemonpid, str(err)
+ )
)
- )
if not wait:
return errors
@@ -1350,7 +1365,7 @@ class Router(Node):
term = topo_terminal if topo_terminal else "xterm"
makeTerm(self, title=title if title else cmd, term=term, cmd=cmd)
else:
- nscmd = "sudo nsenter -m -n -t {} {}".format(self.pid, cmd)
+ nscmd = self.ns_cmd + cmd
if "TMUX" in os.environ:
self.cmd("tmux select-layout main-horizontal")
wcmd = "tmux split-window -h"
@@ -1451,11 +1466,13 @@ class Router(Node):
def startRouterDaemons(self, daemons=None):
"Starts all FRR daemons for this router."
+ asan_abort = g_extra_config["asan_abort"]
gdb_breakpoints = g_extra_config["gdb_breakpoints"]
gdb_daemons = g_extra_config["gdb_daemons"]
gdb_routers = g_extra_config["gdb_routers"]
valgrind_extra = g_extra_config["valgrind_extra"]
valgrind_memleaks = g_extra_config["valgrind_memleaks"]
+ strace_daemons = g_extra_config["strace_daemons"]
bundle_data = ""
@@ -1482,7 +1499,6 @@ class Router(Node):
os.path.join(self.daemondir, "bgpd") + " -v"
).split()[2]
logger.info("{}: running version: {}".format(self.name, self.version))
-
# If `daemons` was specified then some upper API called us with
# specific daemons, otherwise just use our own configuration.
daemons_list = []
@@ -1506,13 +1522,20 @@ class Router(Node):
else:
binary = os.path.join(self.daemondir, daemon)
- cmdenv = "ASAN_OPTIONS=log_path={0}.asan".format(daemon)
+ cmdenv = "ASAN_OPTIONS="
+ if asan_abort:
+ cmdenv = "abort_on_error=1:"
+ cmdenv += "log_path={0}/{1}.{2}.asan ".format(self.logdir, self.name, daemon)
+
if valgrind_memleaks:
this_dir = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
supp_file = os.path.abspath(os.path.join(this_dir, "../../../tools/valgrind.supp"))
cmdenv += " /usr/bin/valgrind --num-callers=50 --log-file={1}/{2}.valgrind.{0}.%p --leak-check=full --suppressions={3}".format(daemon, self.logdir, self.name, supp_file)
if valgrind_extra:
cmdenv += "--gen-suppressions=all --expensive-definedness-checks=yes"
+ elif daemon in strace_daemons or "all" in strace_daemons:
+ cmdenv = "strace -f -D -o {1}/{2}.strace.{0} ".format(daemon, self.logdir, self.name)
+
cmdopt = "{} --log file:{}.log --log-level debug".format(
daemon_opts, daemon
)
diff --git a/tests/topotests/msdp_mesh_topo1/r1/pimd.conf b/tests/topotests/msdp_mesh_topo1/r1/pimd.conf
index 30cecee9e1..c2ffed4762 100644
--- a/tests/topotests/msdp_mesh_topo1/r1/pimd.conf
+++ b/tests/topotests/msdp_mesh_topo1/r1/pimd.conf
@@ -10,6 +10,7 @@ interface r1-eth1
ip igmp
!
ip pim rp 10.254.254.1
+ip pim join-prune-interval 5
ip msdp timers 10 20 3
ip msdp mesh-group mg-1 source 10.254.254.1
ip msdp mesh-group mg-1 member 10.254.254.2
diff --git a/tests/topotests/msdp_mesh_topo1/r2/pimd.conf b/tests/topotests/msdp_mesh_topo1/r2/pimd.conf
index a51c6d58c7..1719a17007 100644
--- a/tests/topotests/msdp_mesh_topo1/r2/pimd.conf
+++ b/tests/topotests/msdp_mesh_topo1/r2/pimd.conf
@@ -9,6 +9,7 @@ interface r2-eth1
ip pim
!
ip pim rp 10.254.254.2
+ip pim join-prune-interval 5
ip msdp timers 10 20 3
ip msdp mesh-group mg-1 source 10.254.254.2
ip msdp mesh-group mg-1 member 10.254.254.1
diff --git a/tests/topotests/msdp_mesh_topo1/r3/pimd.conf b/tests/topotests/msdp_mesh_topo1/r3/pimd.conf
index 663f78620e..2748a55d83 100644
--- a/tests/topotests/msdp_mesh_topo1/r3/pimd.conf
+++ b/tests/topotests/msdp_mesh_topo1/r3/pimd.conf
@@ -9,6 +9,7 @@ interface r3-eth1
ip pim
ip igmp
!
+ip pim join-prune-interval 5
ip pim rp 10.254.254.3
ip msdp timers 10 20 3
ip msdp mesh-group mg-1 source 10.254.254.3
diff --git a/tests/topotests/msdp_topo1/r1/pimd.conf b/tests/topotests/msdp_topo1/r1/pimd.conf
index fc289031f4..4274315271 100644
--- a/tests/topotests/msdp_topo1/r1/pimd.conf
+++ b/tests/topotests/msdp_topo1/r1/pimd.conf
@@ -19,3 +19,4 @@ ip msdp timers 10 20 3
ip msdp peer 192.168.0.2 source 192.168.0.1
ip msdp peer 192.168.1.2 source 192.168.1.1
ip pim rp 10.254.254.1
+ip pim join-prune-interval 5
diff --git a/tests/topotests/msdp_topo1/r2/pimd.conf b/tests/topotests/msdp_topo1/r2/pimd.conf
index ffa80b12d3..a4a69bf05c 100644
--- a/tests/topotests/msdp_topo1/r2/pimd.conf
+++ b/tests/topotests/msdp_topo1/r2/pimd.conf
@@ -15,3 +15,4 @@ ip msdp timers 10 20 3
ip msdp peer 192.168.0.1 source 192.168.0.2
ip msdp peer 192.168.2.2 source 192.168.2.1
ip pim rp 10.254.254.2
+ip pim join-prune-interval 5
diff --git a/tests/topotests/msdp_topo1/r3/pimd.conf b/tests/topotests/msdp_topo1/r3/pimd.conf
index ab12f0573a..db94447c76 100644
--- a/tests/topotests/msdp_topo1/r3/pimd.conf
+++ b/tests/topotests/msdp_topo1/r3/pimd.conf
@@ -15,3 +15,4 @@ ip msdp timers 10 20 3
ip msdp peer 192.168.1.1 source 192.168.1.2
ip msdp peer 192.168.3.2 source 192.168.3.1
ip pim rp 10.254.254.3
+ip pim join-prune-interval 5
diff --git a/tests/topotests/msdp_topo1/r4/pimd.conf b/tests/topotests/msdp_topo1/r4/pimd.conf
index b2e05cb3cb..e9bb59054c 100644
--- a/tests/topotests/msdp_topo1/r4/pimd.conf
+++ b/tests/topotests/msdp_topo1/r4/pimd.conf
@@ -19,3 +19,4 @@ ip msdp timers 10 20 3
ip msdp peer 192.168.2.1 source 192.168.2.2
ip msdp peer 192.168.3.1 source 192.168.3.2
ip pim rp 10.254.254.4
+ip pim join-prune-interval 5
diff --git a/tests/topotests/ospf_gr_topo1/__init__.py b/tests/topotests/ospf_gr_topo1/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/__init__.py
diff --git a/tests/topotests/ospf_gr_topo1/rt1/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt1/ospfd.conf
new file mode 100644
index 0000000000..9c04b74d35
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt1/ospfd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt1
+log file ospfd.log
+log commands
+!
+debug ospf zebra
+debug ospf event
+debug ospf lsa
+debug ospf te
+debug ospf packet all
+debug ospf packet ls-update detail
+debug ospf ism
+debug ospf nsm
+debug ospf nssa
+debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 1
+!
+interface eth-rt2
+ ip ospf network point-to-point
+ ip ospf area 1
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 1.1.1.1
+ capability opaque
+ redistribute connected
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_database.json
new file mode 100644
index 0000000000..d01ac74c17
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_database.json
@@ -0,0 +1,98 @@
+{
+ "routerId":"1.1.1.1",
+ "areas":{
+ "0.0.0.1":{
+ "routerLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"1.1.1.1",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "numOfRouterLinks":2
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"2.2.2.2\/32"
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"3.3.3.3\/32"
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"4.4.4.4\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"6.6.6.6\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.2.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.2.0\/24"
+ },
+ {
+ "lsId":"10.0.3.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.3.0\/24"
+ },
+ {
+ "lsId":"10.0.4.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.4.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.5.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"2.2.2.2"
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"172.16.1.0",
+ "advertisedRouter":"1.1.1.1",
+ "metricType":"E2",
+ "route":"172.16.1.0\/24",
+ "tag":0
+ },
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000000..ed290323a4
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json
@@ -0,0 +1,11 @@
+{
+ "neighbors":{
+ "2.2.2.2":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.1.2",
+ "ifaceName":"eth-rt2:10.0.1.1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_route.json
new file mode 100644
index 0000000000..548ca1e2d1
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_route.json
@@ -0,0 +1,180 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "6.6.6.6":{
+ "routeType":"R ",
+ "cost":30,
+ "area":"0.0.0.1",
+ "IA":true,
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "192.168.1.0\/24":{
+ "routeType":"N E2",
+ "cost":40,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt1/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt1/show_ip_route.json
new file mode 100644
index 0000000000..3dce1eee3e
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt1/show_ip_route.json
@@ -0,0 +1,210 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt1/zebra.conf b/tests/topotests/ospf_gr_topo1/rt1/zebra.conf
new file mode 100644
index 0000000000..183cd3df48
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt1/zebra.conf
@@ -0,0 +1,23 @@
+password 1
+hostname rt1
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 1.1.1.1/32
+!
+interface stub1
+ ip address 172.16.1.1/24
+!
+interface eth-rt2
+ ip address 10.0.1.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt2/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt2/ospfd.conf
new file mode 100644
index 0000000000..922db8c8cc
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt2/ospfd.conf
@@ -0,0 +1,37 @@
+password 1
+hostname rt2
+log file ospfd.log
+log commands
+!
+debug ospf zebra
+debug ospf event
+debug ospf lsa
+debug ospf te
+debug ospf packet all
+debug ospf packet ls-update detail
+debug ospf ism
+debug ospf nsm
+debug ospf nssa
+debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 0
+!
+interface eth-rt1
+ ip ospf network point-to-point
+ ip ospf area 1
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+interface eth-rt3
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 2.2.2.2
+ capability opaque
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_database.json
new file mode 100644
index 0000000000..40c3e82d6a
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_database.json
@@ -0,0 +1,160 @@
+{
+ "routerId":"2.2.2.2",
+ "areas":{
+ "0.0.0.0":{
+ "routerLinkStates":[
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"3.3.3.3",
+ "numOfRouterLinks":7
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.5.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2"
+ }
+ ]
+ },
+ "0.0.0.1":{
+ "routerLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"1.1.1.1",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "numOfRouterLinks":2
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"2.2.2.2\/32"
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"3.3.3.3\/32"
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"4.4.4.4\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"6.6.6.6\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.2.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.2.0\/24"
+ },
+ {
+ "lsId":"10.0.3.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.3.0\/24"
+ },
+ {
+ "lsId":"10.0.4.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.4.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.5.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"2.2.2.2"
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"172.16.1.0",
+ "advertisedRouter":"1.1.1.1",
+ "metricType":"E2",
+ "route":"172.16.1.0\/24",
+ "tag":0
+ },
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000000..4fe92b0b98
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json
@@ -0,0 +1,18 @@
+{
+ "neighbors":{
+ "1.1.1.1":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.1.1",
+ "ifaceName":"eth-rt1:10.0.1.2"
+ }
+ ],
+ "3.3.3.3":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.2.3",
+ "ifaceName":"eth-rt3:10.0.2.2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_route.json
new file mode 100644
index 0000000000..4accb2ba4a
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_route.json
@@ -0,0 +1,201 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "via":"eth-rt1"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt1"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "1.1.1.1":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "via":"eth-rt1"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "6.6.6.6":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "via":"eth-rt1"
+ }
+ ]
+ },
+ "192.168.1.0\/24":{
+ "routeType":"N E2",
+ "cost":30,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt2/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt2/show_ip_route.json
new file mode 100644
index 0000000000..8989a45765
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt2/show_ip_route.json
@@ -0,0 +1,224 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt1"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "172.16.1.0\/24":[
+ {
+ "prefix":"172.16.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt1"
+ }
+ ]
+ }
+ ],
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt2/zebra.conf b/tests/topotests/ospf_gr_topo1/rt2/zebra.conf
new file mode 100644
index 0000000000..8bde98ad44
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt2/zebra.conf
@@ -0,0 +1,23 @@
+password 1
+hostname rt2
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 2.2.2.2/32
+!
+interface eth-rt1
+ ip address 10.0.1.2/24
+!
+interface eth-rt3
+ ip address 10.0.2.2/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt3/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt3/ospfd.conf
new file mode 100644
index 0000000000..51e48f13da
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt3/ospfd.conf
@@ -0,0 +1,43 @@
+password 1
+hostname rt3
+log file ospfd.log
+log commands
+!
+debug ospf zebra
+debug ospf event
+debug ospf lsa
+debug ospf te
+debug ospf packet all
+debug ospf packet ls-update detail
+debug ospf ism
+debug ospf nsm
+debug ospf nssa
+debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 0
+!
+interface eth-rt2
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+interface eth-rt4
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+interface eth-rt6
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 3.3.3.3
+ capability opaque
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_database.json
new file mode 100644
index 0000000000..1fc5b546e4
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_database.json
@@ -0,0 +1,83 @@
+{
+ "routerId":"3.3.3.3",
+ "areas":{
+ "0.0.0.0":{
+ "routerLinkStates":[
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"3.3.3.3",
+ "numOfRouterLinks":7
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.5.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2"
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"172.16.1.0",
+ "advertisedRouter":"1.1.1.1",
+ "metricType":"E2",
+ "route":"172.16.1.0\/24",
+ "tag":0
+ },
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000000..e3c36ab9a3
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json
@@ -0,0 +1,25 @@
+{
+ "neighbors":{
+ "2.2.2.2":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.2.2",
+ "ifaceName":"eth-rt2:10.0.2.3"
+ }
+ ],
+ "4.4.4.4":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.3.4",
+ "ifaceName":"eth-rt4:10.0.3.3"
+ }
+ ],
+ "6.6.6.6":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.4.6",
+ "ifaceName":"eth-rt6:10.0.4.3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_route.json
new file mode 100644
index 0000000000..b2f37e25a1
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_route.json
@@ -0,0 +1,214 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "1.1.1.1":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "IA":true,
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "6.6.6.6":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "192.168.1.0\/24":{
+ "routeType":"N E2",
+ "cost":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "via":"eth-rt6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt3/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt3/show_ip_route.json
new file mode 100644
index 0000000000..c9a1e18b92
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt3/show_ip_route.json
@@ -0,0 +1,223 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "172.16.1.0\/24":[
+ {
+ "prefix":"172.16.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt3/zebra.conf b/tests/topotests/ospf_gr_topo1/rt3/zebra.conf
new file mode 100644
index 0000000000..dfd89cbe5b
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt3/zebra.conf
@@ -0,0 +1,26 @@
+password 1
+hostname rt3
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 3.3.3.3/32
+!
+interface eth-rt2
+ ip address 10.0.2.3/24
+!
+interface eth-rt4
+ ip address 10.0.3.3/24
+!
+interface eth-rt6
+ ip address 10.0.4.3/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt4/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt4/ospfd.conf
new file mode 100644
index 0000000000..a54f27a1d7
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt4/ospfd.conf
@@ -0,0 +1,37 @@
+password 1
+hostname rt4
+log file ospfd.log
+log commands
+!
+debug ospf zebra
+debug ospf event
+debug ospf lsa
+debug ospf te
+debug ospf packet all
+debug ospf packet ls-update detail
+debug ospf ism
+debug ospf nsm
+debug ospf nssa
+debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 0
+!
+interface eth-rt3
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+interface eth-rt5
+ ip ospf network point-to-point
+ ip ospf area 2
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 4.4.4.4
+ capability opaque
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_database.json
new file mode 100644
index 0000000000..87b80414c9
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_database.json
@@ -0,0 +1,164 @@
+{
+ "routerId":"4.4.4.4",
+ "areas":{
+ "0.0.0.0":{
+ "routerLinkStates":[
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"3.3.3.3",
+ "numOfRouterLinks":7
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.5.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2"
+ }
+ ]
+ },
+ "0.0.0.2":{
+ "routerLinkStates":[
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "numOfRouterLinks":2
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"5.5.5.5",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"2.2.2.2\/32"
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"3.3.3.3\/32"
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"4.4.4.4\/32"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"6.6.6.6\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.2.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.2.0\/24"
+ },
+ {
+ "lsId":"10.0.3.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.3.0\/24"
+ },
+ {
+ "lsId":"10.0.4.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.4.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"4.4.4.4"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"4.4.4.4"
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"172.16.1.0",
+ "advertisedRouter":"1.1.1.1",
+ "metricType":"E2",
+ "route":"172.16.1.0\/24",
+ "tag":0
+ },
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000000..2123ecb8da
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json
@@ -0,0 +1,18 @@
+{
+ "neighbors":{
+ "3.3.3.3":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.3.3",
+ "ifaceName":"eth-rt3:10.0.3.4"
+ }
+ ],
+ "5.5.5.5":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.5.5",
+ "ifaceName":"eth-rt5:10.0.5.4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_route.json
new file mode 100644
index 0000000000..04e318aef0
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_route.json
@@ -0,0 +1,202 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.5",
+ "via":"eth-rt5"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt5"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "1.1.1.1":{
+ "routeType":"R ",
+ "cost":30,
+ "area":"0.0.0.0",
+ "IA":true,
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "6.6.6.6":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":30,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "192.168.1.0\/24":{
+ "routeType":"N E2",
+ "cost":30,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt4/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt4/show_ip_route.json
new file mode 100644
index 0000000000..8058f8f431
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt4/show_ip_route.json
@@ -0,0 +1,224 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "172.16.1.0\/24":[
+ {
+ "prefix":"172.16.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt4/zebra.conf b/tests/topotests/ospf_gr_topo1/rt4/zebra.conf
new file mode 100644
index 0000000000..f399b29f3f
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt4/zebra.conf
@@ -0,0 +1,23 @@
+password 1
+hostname rt4
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 4.4.4.4/32
+!
+interface eth-rt3
+ ip address 10.0.3.4/24
+!
+interface eth-rt5
+ ip address 10.0.5.4/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt5/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt5/ospfd.conf
new file mode 100644
index 0000000000..724af0e97c
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt5/ospfd.conf
@@ -0,0 +1,31 @@
+password 1
+hostname rt5
+log file ospfd.log
+log commands
+!
+debug ospf zebra
+debug ospf event
+debug ospf lsa
+debug ospf te
+debug ospf packet all
+debug ospf packet ls-update detail
+debug ospf ism
+debug ospf nsm
+debug ospf nssa
+debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 2
+!
+interface eth-rt4
+ ip ospf network point-to-point
+ ip ospf area 2
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 5.5.5.5
+ capability opaque
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_database.json
new file mode 100644
index 0000000000..aeb8604473
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_database.json
@@ -0,0 +1,102 @@
+{
+ "routerId":"5.5.5.5",
+ "areas":{
+ "0.0.0.2":{
+ "routerLinkStates":[
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "numOfRouterLinks":2
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"5.5.5.5",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"2.2.2.2\/32"
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"3.3.3.3\/32"
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"4.4.4.4\/32"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"6.6.6.6\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.2.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.2.0\/24"
+ },
+ {
+ "lsId":"10.0.3.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.3.0\/24"
+ },
+ {
+ "lsId":"10.0.4.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.4.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"4.4.4.4"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"4.4.4.4"
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"172.16.1.0",
+ "advertisedRouter":"1.1.1.1",
+ "metricType":"E2",
+ "route":"172.16.1.0\/24",
+ "tag":0
+ },
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000000..6440b67698
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json
@@ -0,0 +1,11 @@
+{
+ "neighbors":{
+ "4.4.4.4":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.5.4",
+ "ifaceName":"eth-rt4:10.0.5.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_route.json
new file mode 100644
index 0000000000..e7f712ea6b
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_route.json
@@ -0,0 +1,203 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "1.1.1.1":{
+ "routeType":"R ",
+ "cost":40,
+ "area":"0.0.0.2",
+ "IA":true,
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.2",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "6.6.6.6":{
+ "routeType":"R ",
+ "cost":30,
+ "area":"0.0.0.2",
+ "IA":true,
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":40,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "192.168.1.0\/24":{
+ "routeType":"N E2",
+ "cost":40,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt5/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt5/show_ip_route.json
new file mode 100644
index 0000000000..9896839440
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt5/show_ip_route.json
@@ -0,0 +1,225 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "172.16.1.0\/24":[
+ {
+ "prefix":"172.16.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt5/zebra.conf b/tests/topotests/ospf_gr_topo1/rt5/zebra.conf
new file mode 100644
index 0000000000..49a1c05a6d
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt5/zebra.conf
@@ -0,0 +1,20 @@
+password 1
+hostname rt5
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 5.5.5.5/32
+!
+interface eth-rt4
+ ip address 10.0.5.5/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt6/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt6/ospfd.conf
new file mode 100644
index 0000000000..0b9b83bcd2
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt6/ospfd.conf
@@ -0,0 +1,38 @@
+password 1
+hostname rt6
+log file ospfd.log
+log commands
+!
+debug ospf zebra
+debug ospf event
+debug ospf lsa
+debug ospf te
+debug ospf packet all
+debug ospf packet ls-update detail
+debug ospf ism
+debug ospf nsm
+debug ospf nssa
+debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 0
+!
+interface eth-rt3
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+interface eth-rt7
+ ip ospf network point-to-point
+ ip ospf area 3
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 6.6.6.6
+ capability opaque
+ area 3 nssa
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_database.json
new file mode 100644
index 0000000000..294b2c904e
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_database.json
@@ -0,0 +1,168 @@
+{
+ "routerId":"6.6.6.6",
+ "areas":{
+ "0.0.0.0":{
+ "routerLinkStates":[
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"3.3.3.3",
+ "numOfRouterLinks":7
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.5.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2"
+ }
+ ]
+ },
+ "0.0.0.3":{
+ "routerLinkStates":[
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "numOfRouterLinks":2
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"7.7.7.7",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"0.0.0.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"0.0.0.0\/0"
+ },
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"2.2.2.2\/32"
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"3.3.3.3\/32"
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"4.4.4.4\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"6.6.6.6\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.2.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.2.0\/24"
+ },
+ {
+ "lsId":"10.0.3.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.3.0\/24"
+ },
+ {
+ "lsId":"10.0.4.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.4.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.5.0\/24"
+ }
+ ],
+ "nssaExternalLinkStates":[
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"7.7.7.7",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"172.16.1.0",
+ "advertisedRouter":"1.1.1.1",
+ "metricType":"E2",
+ "route":"172.16.1.0\/24",
+ "tag":0
+ },
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000000..d815c23927
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json
@@ -0,0 +1,18 @@
+{
+ "neighbors":{
+ "3.3.3.3":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.4.3",
+ "ifaceName":"eth-rt3:10.0.4.6"
+ }
+ ],
+ "7.7.7.7":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.6.7",
+ "ifaceName":"eth-rt7:10.0.6.6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_route.json
new file mode 100644
index 0000000000..d9009724d5
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_route.json
@@ -0,0 +1,214 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.7",
+ "via":"eth-rt7"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt7"
+ }
+ ]
+ },
+ "1.1.1.1":{
+ "routeType":"R ",
+ "cost":30,
+ "area":"0.0.0.0",
+ "IA":true,
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "7.7.7.7":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.3",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.6.7",
+ "via":"eth-rt7"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":30,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "192.168.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "nexthops":[
+ {
+ "ip":"10.0.6.7",
+ "via":"eth-rt7"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt6/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt6/show_ip_route.json
new file mode 100644
index 0000000000..dd95f1fab1
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt6/show_ip_route.json
@@ -0,0 +1,224 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.6.7",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt7"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt7"
+ }
+ ]
+ }
+ ],
+ "172.16.1.0\/24":[
+ {
+ "prefix":"172.16.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.6.7",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt7"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt6/zebra.conf b/tests/topotests/ospf_gr_topo1/rt6/zebra.conf
new file mode 100644
index 0000000000..d6a8f52b3a
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt6/zebra.conf
@@ -0,0 +1,23 @@
+password 1
+hostname rt6
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 6.6.6.6/32
+!
+interface eth-rt3
+ ip address 10.0.4.6/24
+!
+interface eth-rt7
+ ip address 10.0.6.6/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt7/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt7/ospfd.conf
new file mode 100644
index 0000000000..49db254410
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt7/ospfd.conf
@@ -0,0 +1,33 @@
+password 1
+hostname rt7
+log file ospfd.log
+log commands
+!
+debug ospf zebra
+debug ospf event
+debug ospf lsa
+debug ospf te
+debug ospf packet all
+debug ospf packet ls-update detail
+debug ospf ism
+debug ospf nsm
+debug ospf nssa
+debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 3
+!
+interface eth-rt6
+ ip ospf network point-to-point
+ ip ospf area 3
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 7.7.7.7
+ capability opaque
+ redistribute connected
+ area 3 nssa
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_database.json
new file mode 100644
index 0000000000..4916fba9d4
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_database.json
@@ -0,0 +1,99 @@
+{
+ "routerId":"7.7.7.7",
+ "areas":{
+ "0.0.0.3":{
+ "routerLinkStates":[
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "numOfRouterLinks":2
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"7.7.7.7",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"0.0.0.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"0.0.0.0\/0"
+ },
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"2.2.2.2\/32"
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"3.3.3.3\/32"
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"4.4.4.4\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"6.6.6.6\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.2.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.2.0\/24"
+ },
+ {
+ "lsId":"10.0.3.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.3.0\/24"
+ },
+ {
+ "lsId":"10.0.4.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.4.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.5.0\/24"
+ }
+ ],
+ "nssaExternalLinkStates":[
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"7.7.7.7",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"7.7.7.7",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000000..2254aea9a6
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json
@@ -0,0 +1,11 @@
+{
+ "neighbors":{
+ "6.6.6.6":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.6.6",
+ "ifaceName":"eth-rt6:10.0.6.7"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_route.json
new file mode 100644
index 0000000000..89bad320bb
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_route.json
@@ -0,0 +1,168 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt6"
+ }
+ ]
+ },
+ "6.6.6.6":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.3",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt7/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt7/show_ip_route.json
new file mode 100644
index 0000000000..0fb906b76b
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt7/show_ip_route.json
@@ -0,0 +1,210 @@
+{
+ "0.0.0.0\/0":[
+ {
+ "prefix":"0.0.0.0\/0",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":11,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt7/zebra.conf b/tests/topotests/ospf_gr_topo1/rt7/zebra.conf
new file mode 100644
index 0000000000..c481e4532b
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt7/zebra.conf
@@ -0,0 +1,23 @@
+password 1
+hostname rt7
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 7.7.7.7/32
+!
+interface stub1
+ ip address 192.168.1.1/24
+!
+interface eth-rt6
+ ip address 10.0.6.7/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py b/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py
new file mode 100755
index 0000000000..0507c2d516
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py
@@ -0,0 +1,393 @@
+#!/usr/bin/env python
+
+#
+# test_ospf_gr_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# 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_ospf_gr_topo1.py:
+
+ +---------+
+ | RT1 |
+ | 1.1.1.1 |
+ +---------+
+ |eth-rt2
+ |
+ |10.0.1.0/24
+ |
+ |eth-rt1
+ +---------+
+ | RT2 |
+ | 2.2.2.2 |
+ +---------+
+ |eth-rt3
+ |
+ |10.0.2.0/24
+ |
+ |eth-rt2
+ +---------+
+ | RT3 |
+ | 3.3.3.3 |
+ +---------+
+ eth-rt4| |eth-rt6
+ | |
+ 10.0.3.0/24 | | 10.0.4.0/24
+ +---------+ +--------+
+ | |
+ |eth-rt3 |eth-rt3
+ +---------+ +---------+
+ | RT4 | | RT6 |
+ | 4.4.4.4 | | 6.6.6.6 |
+ +---------+ +---------+
+ |eth-rt5 |eth-rt7
+ | |
+ |10.0.5.0/24 |10.0.6.0/24
+ | |
+ |eth-rt4 |eth-rt6
+ +---------+ +---------+
+ | RT5 | | RT7 |
+ | 5.5.5.5 | | 7.7.7.7 |
+ +---------+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+import re
+import tempfile
+from time import sleep
+from functools import partial
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# 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 (
+ kill_router_daemons,
+ start_router_daemons,
+)
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+pytestmark = [pytest.mark.ospfd]
+
+# Global multi-dimensional dictionary containing all expected outputs
+outputs = {}
+
+
+class TemplateTopo(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1")
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["rt1"], nodeif="stub1")
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt3")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt2")
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt4")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt3")
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt3")
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4")
+
+ switch = tgen.add_switch("s7")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt7")
+ switch.add_link(tgen.gears["rt7"], nodeif="eth-rt6")
+
+ switch = tgen.add_switch("s8")
+ switch.add_link(tgen.gears["rt7"], nodeif="stub1")
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(TemplateTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def router_compare_json_output(rname, command, reference, tries):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=tries, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def check_routers(initial_convergence=False, exiting=None, restarting=None):
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
+ # Check the RIB first, which should be preserved across restarts in
+ # all routers of the routing domain.
+ if initial_convergence == True:
+ tries = 240
+ else:
+ tries = 1
+ router_compare_json_output(
+ rname, "show ip route ospf json", "show_ip_route.json", tries
+ )
+
+ # Check that all adjacencies are up and running (except when there's
+ # an OSPF instance that is shutting down).
+ if exiting == None:
+ tries = 240
+ router_compare_json_output(
+ rname, "show ip ospf neighbor json", "show_ip_ospf_neighbor.json", tries
+ )
+
+ # Check the OSPF RIB and LSDB.
+ # In the restarting router, wait up to one minute for the LSDB to converge.
+ if exiting != rname:
+ if initial_convergence == True or restarting == rname:
+ tries = 240
+ else:
+ tries = 1
+ router_compare_json_output(
+ rname, "show ip ospf database json", "show_ip_ospf_database.json", tries
+ )
+ router_compare_json_output(
+ rname, "show ip ospf route json", "show_ip_ospf_route.json", tries
+ )
+
+
+#
+# Test initial network convergence
+#
+def test_initial_convergence():
+ logger.info("Test: verify initial network convergence")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_routers(initial_convergence=True)
+
+
+#
+# Test rt1 performing a graceful restart
+#
+def test_gr_rt1():
+ logger.info("Test: verify rt1 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt1"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ sleep(3)
+ kill_router_daemons(tgen, "rt1", ["ospfd"], save_config=False)
+ check_routers(exiting="rt1")
+
+ start_router_daemons(tgen, "rt1", ["ospfd"])
+ check_routers(restarting="rt1")
+
+
+#
+# Test rt2 performing a graceful restart
+#
+def test_gr_rt2():
+ logger.info("Test: verify rt2 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt2"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ sleep(3)
+ kill_router_daemons(tgen, "rt2", ["ospfd"], save_config=False)
+ check_routers(exiting="rt2")
+
+ start_router_daemons(tgen, "rt2", ["ospfd"])
+ check_routers(restarting="rt2")
+
+
+#
+# Test rt3 performing a graceful restart
+#
+def test_gr_rt3():
+ logger.info("Test: verify rt3 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt3"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ sleep(3)
+ kill_router_daemons(tgen, "rt3", ["ospfd"], save_config=False)
+ check_routers(exiting="rt3")
+
+ start_router_daemons(tgen, "rt3", ["ospfd"])
+ check_routers(restarting="rt3")
+
+
+#
+# Test rt4 performing a graceful restart
+#
+def test_gr_rt4():
+ logger.info("Test: verify rt4 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt4"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ sleep(3)
+ kill_router_daemons(tgen, "rt4", ["ospfd"], save_config=False)
+ check_routers(exiting="rt4")
+
+ start_router_daemons(tgen, "rt4", ["ospfd"])
+ check_routers(restarting="rt4")
+
+
+#
+# Test rt5 performing a graceful restart
+#
+def test_gr_rt5():
+ logger.info("Test: verify rt5 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt5"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ sleep(3)
+ kill_router_daemons(tgen, "rt5", ["ospfd"], save_config=False)
+ check_routers(exiting="rt5")
+
+ start_router_daemons(tgen, "rt5", ["ospfd"])
+ check_routers(restarting="rt5")
+
+
+#
+# Test rt6 performing a graceful restart
+#
+def test_gr_rt6():
+ logger.info("Test: verify rt6 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt6"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ sleep(3)
+ kill_router_daemons(tgen, "rt6", ["ospfd"], save_config=False)
+ check_routers(exiting="rt6")
+
+ start_router_daemons(tgen, "rt6", ["ospfd"])
+ check_routers(restarting="rt6")
+
+
+#
+# Test rt7 performing a graceful restart
+#
+def test_gr_rt7():
+ logger.info("Test: verify rt7 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt7"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ sleep(3)
+ kill_router_daemons(tgen, "rt7", ["ospfd"], save_config=False)
+ check_routers(exiting="rt7")
+
+ start_router_daemons(tgen, "rt7", ["ospfd"])
+ check_routers(restarting="rt7")
+
+
+# Memory leak test template
+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))
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json
new file mode 100644
index 0000000000..c928093925
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json
@@ -0,0 +1,347 @@
+{
+ "address_types": [
+ "ipv6"
+ ],
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link4": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link5": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link6": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link7": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r1-link1": {
+ "nbr": "r1"
+ },
+ "r1-link2": {
+ "nbr": "r1"
+ },
+ "r1-link3": {
+ "nbr": "r1"
+ },
+ "r1-link4": {
+ "nbr": "r1"
+ },
+ "r1-link5": {
+ "nbr": "r1"
+ },
+ "r1-link6": {
+ "nbr": "r1"
+ },
+ "r1-link7": {
+ "nbr": "r1"
+ },
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link4": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link5": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link6": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link7": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r0-link1": {
+ "nbr": "r0"
+ },
+ "r0-link2": {
+ "nbr": "r0"
+ },
+ "r0-link3": {
+ "nbr": "r0"
+ },
+ "r0-link4": {
+ "nbr": "r0"
+ },
+ "r0-link5": {
+ "nbr": "r0"
+ },
+ "r0-link6": {
+ "nbr": "r0"
+ },
+ "r0-link7": {
+ "nbr": "r0"
+ },
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR1",
+ "ospf": {
+ "area": "0.0.0.0"
+ },
+ "ospf6": {
+ "area": "0.0.0.0"
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_routemaps.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_routemaps.json
new file mode 100644
index 0000000000..226f84f320
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_routemaps.json
@@ -0,0 +1,137 @@
+{
+ "address_types": ["ipv6"],
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {"ipv6": "fd00::", "v6mask": 64},
+ "lo_prefix": {"ipv6": "2001:db8:f::", "v6mask": 128},
+ "routers": {
+ "r0": {
+ "links": {
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {"r1": {}, "r2": {}, "r3": {}}
+ }
+ },
+ "r1": {
+ "links": {
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "neighbors": {"r0": {}, "r2": {}, "r3": {}}
+ }
+ },
+ "r2": {
+ "links": {
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "neighbors": {"r1": {}, "r0": {}, "r3": {}}
+ }
+ },
+ "r3": {
+ "links": {
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.3",
+ "neighbors": {"r0": {}, "r1": {}, "r2": {}}
+ }
+ }
+ }
+}
diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py
new file mode 100644
index 0000000000..a439375be8
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py
@@ -0,0 +1,520 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2021 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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+import json
+from copy import deepcopy
+from ipaddress import IPv4Address
+from lib.topotest import frr_unicode
+
+# 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, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from mininet.topo import Topo
+from lib.topogen import Topogen, get_topogen
+import ipaddress
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ step,
+ create_route_maps,
+ shutdown_bringup_interface,
+ create_interfaces_cfg,
+ topo_daemons,
+ get_frr_ipv6_linklocal,
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+from lib.ospf import (
+ verify_ospf6_neighbor,
+ config_ospf_interface,
+ clear_ospf,
+ verify_ospf6_rib,
+ create_router_ospf,
+ verify_ospf6_interface,
+ verify_ospf6_database,
+ config_ospf6_interface,
+)
+
+from ipaddress import IPv6Address
+
+# Global variables
+topo = None
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/ospfv3_ecmp.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ],
+ "ipv6": ["2::1/128", "2::2/128", "2::3/128", "2::4/128", "2::5/128"],
+}
+"""
+TOPOLOGY :
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+TESTCASES :
+1. Verify OSPF ECMP with max path configured as 8 (ECMPconfigured at FRR level)
+2. Verify OSPF ECMP with max path configured as 2 (Edge having 2 uplink ports)
+ """
+
+
+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 topo
+ 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__)
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # 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)
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ 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()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+def red_static(dut, config=True):
+ """Local def for Redstribute static routes inside ospf."""
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "static"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {"redistribute": [{"redist_type": "static", "delete": True}]}
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+
+def red_connected(dut, config=True):
+ """Local def for Redstribute connected routes inside ospf."""
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "connected"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {
+ "redistribute": [{"redist_type": "connected", "del_action": True}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase: Failed \n Error: {}".format(result)
+
+
+def get_llip(onrouter, intf):
+ """
+ API to get the link local ipv6 address of a perticular interface
+
+ Parameters
+ ----------
+ * `fromnode`: Source node
+ * `tonode` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_llip('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) link local ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ tgen = get_topogen()
+ intf = topo["routers"][onrouter]["links"][intf]["interface"]
+ llip = get_frr_ipv6_linklocal(tgen, onrouter, intf)
+ if llip:
+ logger.info("llip ipv6 address to be set as NH is %s", llip)
+ return llip
+ return None
+
+
+def get_glipv6(onrouter, intf):
+ """
+ API to get the global ipv6 address of a perticular interface
+
+ Parameters
+ ----------
+ * `onrouter`: Source node
+ * `intf` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_glipv6('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) global ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0]
+ if glipv6:
+ logger.info("Global ipv6 address to be set as NH is %s", glipv6)
+ return glipv6
+ return None
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospfv3_ecmp_tc16_p0(request):
+ """
+ Verify OSPF ECMP.
+
+ Verify OSPF ECMP with max path configured as 8 (ECMP
+ configured at FRR level)
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step("Configure 8 interfaces between R1 and R2 and enable ospf in area 0.")
+
+ reset_config_on_routers(tgen)
+
+ step("Verify that OSPF is up with 8 neighborship sessions.")
+ dut = "r1"
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Configure a static route in R0 and redistribute in OSPF.")
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ llip = get_llip("r0", "r1-link1")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that route in R2 in stalled with 8 next hops.")
+ nh = []
+ for item in range(1, 7):
+ nh.append(llip)
+
+ llip = get_llip("r0", "r1")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ nh2 = llip
+
+ nh.append(nh2)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("shut no shut all the interfaces on the remote router - R2")
+ dut = "r1"
+ for intfr in range(1, 7):
+ intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route present in OSPF RIB. Error: {}".format(
+ tc_name, result
+ )
+
+ protocol = "ospf"
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name, result)
+
+ for intfr in range(1, 7):
+ intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("shut no shut on all the interfaces on DUT (r1)")
+ for intfr in range(1, 7):
+ intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ for intfr in range(1, 7):
+ intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "Verify that all the neighbours are up and routes are installed"
+ " with 8 next hop in ospf and ip route tables on R1."
+ )
+
+ step("Verify that OSPF is up with 8 neighborship sessions.")
+ dut = "r1"
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_ecmp_tc17_p0(request):
+ """
+ Verify OSPF ECMP.
+
+ Verify OSPF ECMP with max path configured as 2 (Edge having 2 uplink ports)
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step("Configure 2 interfaces between R1 and R2 & enable ospf in area 0.")
+
+ reset_config_on_routers(tgen)
+
+ step("Verify that OSPF is up with 2 neighborship sessions.")
+ dut = "r1"
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Configure a static route in R0 and redistribute in OSPF.")
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that route in R2 in stalled with 2 next hops.")
+
+ llip = get_llip("r0", "r1-link1")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ nh1 = llip
+
+ llip = get_llip("r0", "r1")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ nh2 = llip
+
+ nh = [nh1, nh2]
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure ECMP value as 1.")
+ max_path = {"r1": {"ospf6": {"maximum-paths": 1}}}
+ result = create_router_ospf(tgen, topo, max_path)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ max_path = {"r1": {"ospf6": {"maximum-paths": 2}}}
+ result = create_router_ospf(tgen, topo, max_path)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure cost on R0 as 100")
+ r0_ospf_cost = {"r0": {"links": {"r1": {"ospf6": {"cost": 100}}}}}
+ result = config_ospf6_interface(tgen, topo, r0_ospf_cost)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is 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))
diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py
new file mode 100644
index 0000000000..9ca460e487
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py
@@ -0,0 +1,872 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2021 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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+import json
+from copy import deepcopy
+from ipaddress import IPv4Address
+from lib.topotest import frr_unicode
+
+# 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, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from mininet.topo import Topo
+from lib.topogen import Topogen, get_topogen
+import ipaddress
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ create_prefix_lists,
+ verify_rib,
+ create_static_routes,
+ step,
+ create_route_maps,
+ verify_prefix_lists,
+ get_frr_ipv6_linklocal,
+ topo_daemons,
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+from lib.ospf import (
+ verify_ospf6_neighbor,
+ config_ospf_interface,
+ clear_ospf,
+ verify_ospf6_rib,
+ create_router_ospf,
+ verify_ospf6_interface,
+ verify_ospf6_database,
+ config_ospf6_interface,
+)
+
+from ipaddress import IPv6Address
+
+# Global variables
+topo = None
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/ospfv3_routemaps.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ],
+ "ipv6": ["2::1/128", "2::2/128", "2::3/128", "2::4/128", "2::5/128"],
+}
+
+routerids = ["100.1.1.0", "100.1.1.1", "100.1.1.2", "100.1.1.3"]
+
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+TESTCASES =
+2. Verify OSPF route map support functionality when route map is not
+ configured at system level but configured in OSPF
+4. Verify OSPF route map support functionality
+ when route map actions are toggled.
+5. Verify OSPF route map support functionality with multiple sequence
+ numbers in a single route-map for different match/set clauses.
+6. Verify OSPF route map support functionality when we add/remove route-maps
+ with multiple set clauses and without any match statement.(Set only)
+7. Verify OSPF route map support functionality when we
+ add/remove route-maps with multiple match clauses and without
+ any set statement.(Match only)
+8. Verify OSPF route map applied to ospf redistribution with ipv6 prefix list
+ """
+
+
+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 topo
+ 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__)
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # 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)
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ 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()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospfv3_routemaps_functionality_tc20_p0(request):
+ """
+ OSPF route map support functionality.
+
+ Verify OSPF route map support functionality when route map is not
+ configured at system level but configured in OSPF
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+
+ reset_config_on_routers(tgen)
+
+ step("Create static routes(10.0.20.1/32 and 10.0.20.2/32) in R0")
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Redistribute to ospf using route map ( non existent route map)")
+ ospf_red_r1 = {
+ "r0": {
+ "ospf6": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv6"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that routes are not allowed in OSPF even tough no "
+ "matching routing map is configured."
+ )
+
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "configure the route map with the same name that is used "
+ "in the ospf with deny rule."
+ )
+
+ # Create route map
+ routemaps = {"r0": {"route_maps": {"rmap_ipv6": [{"action": "deny"}]}}}
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that now route map is activated & routes are denied in OSPF.")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ routemaps = {"r0": {"route_maps": {"rmap_ipv6": [{"action": "deny"}]}}}
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that now route map is activated & routes are denied in OSPF.")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ step("Delete the route map.")
+ # Create route map
+ routemaps = {
+ "r0": {"route_maps": {"rmap_ipv6": [{"action": "deny", "delete": True}]}}
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that routes are allowed in OSPF even tough "
+ "no matching routing map is configured."
+ )
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_routemaps_functionality_tc25_p0(request):
+ """
+ OSPF route map support functionality.
+
+ Verify OSPF route map support functionality
+ when route map actions are toggled.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+
+ reset_config_on_routers(tgen)
+
+ step(
+ "Create static routes(10.0.20.1/32) in R1 and redistribute "
+ "to OSPF using route map."
+ )
+
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r0 = {
+ "r0": {
+ "ospf6": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv6"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step("Configure route map with permit rule")
+ # Create route map
+ routemaps = {"r0": {"route_maps": {"rmap_ipv6": [{"action": "permit"}]}}}
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that route is advertised to R1.")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step("Configure route map with deny rule")
+ # Create route map
+ routemaps = {
+ "r0": {"route_maps": {"rmap_ipv6": [{"seq_id": 10, "action": "deny"}]}}
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Verify that route is not advertised to R1.")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_routemaps_functionality_tc22_p0(request):
+ """
+ OSPF Route map - Multiple sequence numbers.
+
+ Verify OSPF route map support functionality with multiple sequence
+ numbers in a single route-map for different match/set clauses.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure route map with seq number 10 to with ip prefix"
+ " permitting route 10.0.20.1/32 in R1"
+ )
+ step(
+ "Configure route map with seq number 20 to with ip prefix"
+ " permitting route 10.0.20.2/32 in R1"
+ )
+
+ # Create route map
+ input_dict_3 = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {"ipv6": {"prefix_lists": "pf_list_1_ipv6"}},
+ },
+ {
+ "action": "permit",
+ "seq_id": "20",
+ "match": {"ipv6": {"prefix_lists": "pf_list_2_ipv4"}},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv6": [
+ {"seqid": 10, "network": NETWORK["ipv6"][0], "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_2_ipv4": [
+ {"seqid": 10, "network": NETWORK["ipv6"][1], "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure static routes 10.0.20.1/32 and 10.0.20.2 in R1")
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure redistribute static route with route map.")
+ ospf_red_r0 = {
+ "r0": {
+ "ospf6": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv6"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 2,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that both routes are learned in R1 and R2")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r2"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change route map with seq number 20 to deny.")
+ # Create route map
+ input_dict_3 = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "deny",
+ "seq_id": "20",
+ "match": {"ipv6": {"prefix_lists": "pf_list_2_ipv4"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify the route 10.0.20.2/32 is withdrawn and not present "
+ "in the routing table of R0 and R1."
+ )
+
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv6"][1], "next_hop": "Null0"}]}
+ }
+
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_routemaps_functionality_tc24_p0(request):
+ """
+ OSPF Route map - Multiple set clauses.
+
+ Verify OSPF route map support functionality when we
+ add/remove route-maps with multiple match clauses and without
+ any set statement.(Match only)
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+
+ reset_config_on_routers(tgen)
+
+ step(
+ "Create static routes(10.0.20.1/32) in R1 and redistribute to "
+ "OSPF using route map."
+ )
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 1,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r0 = {
+ "r0": {
+ "ospf6": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv6"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that prefix-list is created in R0.")
+ result = verify_prefix_lists(tgen, pfx_list)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "permit",
+ "match": {"ipv6": {"prefix_lists": "pf_list_1_ipv6"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that metric falls back to original metric for ospf routes.")
+ dut = "r1"
+ protocol = "ospf"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Create static routes(10.0.20.1/32) in R1 and redistribute to "
+ "OSPF using route map."
+ )
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][1],
+ "no_of_ip": 1,
+ "next_hop": "Null0",
+ "tag": 1000,
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that prefix-list is created in R0.")
+ result = verify_prefix_lists(tgen, pfx_list)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [{"action": "permit", "match": {"ipv6": {"tag": "1000"}}}]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that metric falls back to original metric for ospf routes.")
+ dut = "r1"
+ protocol = "ospf"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the match clause with tag in route map")
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "permit",
+ "match": {"ipv6": {"tag": "1000", "delete": True}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that metric falls back to original metric for ospf routes.")
+ dut = "r1"
+ protocol = "ospf"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the match clause with metric in route map.")
+
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "permit",
+ "match": {"ipv6": {"prefix_lists": "pf_list_1_ipv6"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is 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))
diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py
index 4aa71bfb16..e01c6d6047 100644
--- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py
@@ -281,6 +281,233 @@ def red_connected(dut, config=True):
# ##################################
# Test cases start here.
# ##################################
+def test_ospfv3_redistribution_tc5_p0(request):
+ """Test OSPF intra area route calculations."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+
+ step("Verify that OSPF neighbors are FULL.")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("verify intra area route is calculated for r0-r3 interface ip in R1")
+ ip = topo["routers"]["r0"]["links"]["r3"]["ipv6"]
+ ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network)
+
+ llip = get_llip("r0", "r1")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, llip)
+
+ nh = llip
+ input_dict = {
+ "r1": {"static_routes": [{"network": ip_net, "no_of_ip": 1, "routeType": "N"}]}
+ }
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the ip address on newly configured loopback of R0")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv6": topo["routers"]["r0"]["links"]["r3"]["ipv6"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name, result)
+
+ step("Add back the deleted ip address on newly configured interface of R0")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv6": topo["routers"]["r0"]["links"]["r3"]["ipv6"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Shut no shut interface on R0")
+ dut = "r0"
+ intf = topo["routers"]["r0"]["links"]["r3"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("un shut the OSPF interface on R0")
+ dut = "r0"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_redistribution_tc6_p0(request):
+ """Test OSPF inter area route calculations."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+
+ step("Verify that OSPF neighbors are FULL.")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("verify intra area route is calculated for r0-r3 interface ip in R1")
+ ip = topo["routers"]["r0"]["links"]["r3"]["ipv6"]
+ ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network)
+ llip = get_llip("r0", "r1")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, llip)
+ nh = llip
+ input_dict = {
+ "r1": {"static_routes": [{"network": ip_net, "no_of_ip": 1, "routeType": "N"}]}
+ }
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the ip address on newly configured loopback of R0")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv6": topo["routers"]["r0"]["links"]["r3"]["ipv6"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name, result)
+
+ step("Add back the deleted ip address on newly configured interface of R0")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv6": topo["routers"]["r0"]["links"]["r3"]["ipv6"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Shut no shut interface on R0")
+ dut = "r0"
+ intf = topo["routers"]["r0"]["links"]["r3"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("Verify that intraroute calculated for R1 intf on R0 is deleted.")
+ dut = "r1"
+
+ step("un shut the OSPF interface on R0")
+ dut = "r0"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
def test_ospfv3_cost_tc52_p0(request):
"""OSPF Cost - verifying ospf interface cost functionality"""
tc_name = request.node.name
@@ -368,7 +595,6 @@ def test_ospfv3_cost_tc52_p0(request):
write_test_footer(tc_name)
-
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py
index a84f1a1eb6..faae4b3e17 100644
--- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py
@@ -54,7 +54,7 @@ from lib.common_config import (
create_route_maps,
shutdown_bringup_interface,
create_interfaces_cfg,
- topo_daemons,
+ topo_daemons
)
from lib.topolog import logger
from lib.topojson import build_topo_from_json, build_config_from_json
diff --git a/tests/topotests/pim_acl/h1/zebra.conf b/tests/topotests/pim_acl/h1/zebra.conf
new file mode 100644
index 0000000000..3d6540d40c
--- /dev/null
+++ b/tests/topotests/pim_acl/h1/zebra.conf
@@ -0,0 +1,10 @@
+!
+hostname h1
+log file zebra.log
+!
+interface h1-eth0
+ description connection to r1 via sw1
+ ip address 192.168.100.10/24
+!
+ip route 0.0.0.0/0 192.168.100.1
+!
diff --git a/tests/topotests/pim_acl/h2/zebra.conf b/tests/topotests/pim_acl/h2/zebra.conf
new file mode 100644
index 0000000000..95342f9e8a
--- /dev/null
+++ b/tests/topotests/pim_acl/h2/zebra.conf
@@ -0,0 +1,8 @@
+hostname h2
+!
+interface h2-eth0
+ description connection to r1 via sw2
+ ip address 192.168.101.2/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/r1/acl_1_pim_join.json b/tests/topotests/pim_acl/r1/acl_1_pim_join.json
new file mode 100644
index 0000000000..1b44b2b5cf
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/acl_1_pim_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.1":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.1",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/acl_2_pim_join.json b/tests/topotests/pim_acl/r1/acl_2_pim_join.json
new file mode 100644
index 0000000000..c020a489a9
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/acl_2_pim_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.17":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.17",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/acl_3_pim_join.json b/tests/topotests/pim_acl/r1/acl_3_pim_join.json
new file mode 100644
index 0000000000..6122f73992
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/acl_3_pim_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.32":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.32",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/acl_4_pim_join.json b/tests/topotests/pim_acl/r1/acl_4_pim_join.json
new file mode 100644
index 0000000000..5f72256ba7
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/acl_4_pim_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.255":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.255",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/acl_5_pim_join.json b/tests/topotests/pim_acl/r1/acl_5_pim_join.json
new file mode 100644
index 0000000000..70021bdbec
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/acl_5_pim_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.97":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.97",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/acl_6_pim_join.json b/tests/topotests/pim_acl/r1/acl_6_pim_join.json
new file mode 100644
index 0000000000..2baac6cb22
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/acl_6_pim_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.70":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.70",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/ospf_neighbor.json b/tests/topotests/pim_acl/r1/ospf_neighbor.json
new file mode 100644
index 0000000000..a8fc093e90
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/ospf_neighbor.json
@@ -0,0 +1,59 @@
+{
+ "neighbors":{
+ "192.168.0.11":[
+ {
+ "priority":10,
+ "state":"Full\/Backup",
+ "address":"192.168.101.11",
+ "ifaceName":"r1-eth1:192.168.101.1",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ],
+ "192.168.0.12":[
+ {
+ "priority":0,
+ "state":"Full\/DROther",
+ "address":"192.168.101.12",
+ "ifaceName":"r1-eth1:192.168.101.1",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ],
+ "192.168.0.13":[
+ {
+ "priority":0,
+ "state":"Full\/DROther",
+ "address":"192.168.101.13",
+ "ifaceName":"r1-eth1:192.168.101.1",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ],
+ "192.168.0.14":[
+ {
+ "priority":0,
+ "state":"Full\/DROther",
+ "address":"192.168.101.14",
+ "ifaceName":"r1-eth1:192.168.101.1",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ],
+ "192.168.0.15":[
+ {
+ "priority":0,
+ "state":"Full\/DROther",
+ "address":"192.168.101.15",
+ "ifaceName":"r1-eth1:192.168.101.1",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/ospfd.conf b/tests/topotests/pim_acl/r1/ospfd.conf
new file mode 100644
index 0000000000..e1f47fb3b1
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/ospfd.conf
@@ -0,0 +1,16 @@
+hostname r1
+!
+debug ospf event
+!
+interface r1-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 20
+!
+router ospf
+ ospf router-id 192.168.0.1
+ passive-interface r1-eth0
+ network 192.168.0.1/32 area 0
+ network 192.168.100.0/24 area 0
+ network 192.168.101.0/24 area 0
+
diff --git a/tests/topotests/pim_acl/r1/pim_neighbor.json b/tests/topotests/pim_acl/r1/pim_neighbor.json
new file mode 100644
index 0000000000..ae95e8db14
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/pim_neighbor.json
@@ -0,0 +1,31 @@
+{
+ "r1-eth0":{
+ },
+ "r1-eth1":{
+ "192.168.101.12":{
+ "interface":"r1-eth1",
+ "neighbor":"192.168.101.12",
+ "drPriority":1
+ },
+ "192.168.101.15":{
+ "interface":"r1-eth1",
+ "neighbor":"192.168.101.15",
+ "drPriority":1
+ },
+ "192.168.101.14":{
+ "interface":"r1-eth1",
+ "neighbor":"192.168.101.14",
+ "drPriority":1
+ },
+ "192.168.101.11":{
+ "interface":"r1-eth1",
+ "neighbor":"192.168.101.11",
+ "drPriority":1
+ },
+ "192.168.101.13":{
+ "interface":"r1-eth1",
+ "neighbor":"192.168.101.13",
+ "drPriority":1
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/pimd.conf b/tests/topotests/pim_acl/r1/pimd.conf
new file mode 100644
index 0000000000..a148c73146
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/pimd.conf
@@ -0,0 +1,31 @@
+hostname r1
+!
+debug igmp events
+debug igmp packets
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.11 prefix-list rp-pl-1
+ip pim rp 192.168.0.12 prefix-list rp-pl-2
+ip pim rp 192.168.0.13 prefix-list rp-pl-3
+ip pim rp 192.168.0.14 prefix-list rp-pl-4
+ip pim rp 192.168.0.15 prefix-list rp-pl-5
+ip pim join-prune-interval 5
+!
+interface r1-eth0
+ ip igmp
+ ip igmp version 2
+ ip pim
+!
+interface r1-eth1
+ ip pim
+!
+ip prefix-list rp-pl-1 seq 10 permit 239.100.0.0/28
+ip prefix-list rp-pl-2 seq 10 permit 239.100.0.17/32
+ip prefix-list rp-pl-3 seq 10 permit 239.100.0.32/27
+ip prefix-list rp-pl-4 seq 10 permit 239.100.0.128/25
+ip prefix-list rp-pl-4 seq 20 permit 239.100.0.96/28
+ip prefix-list rp-pl-5 seq 10 permit 239.100.0.64/28
diff --git a/tests/topotests/pim_acl/r1/zebra.conf b/tests/topotests/pim_acl/r1/zebra.conf
new file mode 100644
index 0000000000..74feb8f6a7
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/zebra.conf
@@ -0,0 +1,18 @@
+!
+hostname r1
+log file zebra.log
+!
+ip forwarding
+ipv6 forwarding
+!
+interface lo
+ ip address 192.168.0.1/32
+!
+interface r1-eth0
+ description connection to h1 via sw1
+ ip address 192.168.100.1/24
+!
+interface r1-eth1
+ description connection to r11/12/13/14/15 via sw2
+ ip address 192.168.101.1/24
+!
diff --git a/tests/topotests/pim_acl/r11/acl_1_pim_join.json b/tests/topotests/pim_acl/r11/acl_1_pim_join.json
new file mode 100644
index 0000000000..289bf51e76
--- /dev/null
+++ b/tests/topotests/pim_acl/r11/acl_1_pim_join.json
@@ -0,0 +1,19 @@
+{
+ "r11-eth0":{
+ "name":"r11-eth0",
+ "state":"up",
+ "address":"192.168.101.11",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.1":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.1",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r11/ospfd.conf b/tests/topotests/pim_acl/r11/ospfd.conf
new file mode 100644
index 0000000000..e107220a4e
--- /dev/null
+++ b/tests/topotests/pim_acl/r11/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r11
+!
+debug ospf event
+!
+interface r11-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 10
+!
+router ospf
+ ospf router-id 192.168.0.11
+ network 192.168.0.11/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_acl/r11/pimd.conf b/tests/topotests/pim_acl/r11/pimd.conf
new file mode 100644
index 0000000000..b1d45205da
--- /dev/null
+++ b/tests/topotests/pim_acl/r11/pimd.conf
@@ -0,0 +1,17 @@
+hostname r11
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.11 239.100.0.0/28
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r11-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_acl/r11/zebra.conf b/tests/topotests/pim_acl/r11/zebra.conf
new file mode 100644
index 0000000000..137706d245
--- /dev/null
+++ b/tests/topotests/pim_acl/r11/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r11
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.11/32
+!
+interface r11-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.11/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/r12/acl_2_pim_join.json b/tests/topotests/pim_acl/r12/acl_2_pim_join.json
new file mode 100644
index 0000000000..76ab7ee701
--- /dev/null
+++ b/tests/topotests/pim_acl/r12/acl_2_pim_join.json
@@ -0,0 +1,19 @@
+{
+ "r12-eth0":{
+ "name":"r12-eth0",
+ "state":"up",
+ "address":"192.168.101.12",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.17":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.17",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r12/ospfd.conf b/tests/topotests/pim_acl/r12/ospfd.conf
new file mode 100644
index 0000000000..f9203c78e4
--- /dev/null
+++ b/tests/topotests/pim_acl/r12/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r12
+!
+debug ospf event
+!
+interface r12-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 0
+!
+router ospf
+ ospf router-id 192.168.0.12
+ network 192.168.0.12/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_acl/r12/pimd.conf b/tests/topotests/pim_acl/r12/pimd.conf
new file mode 100644
index 0000000000..ba9e7d902f
--- /dev/null
+++ b/tests/topotests/pim_acl/r12/pimd.conf
@@ -0,0 +1,17 @@
+hostname r12
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.12 239.100.0.17/32
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r12-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_acl/r12/zebra.conf b/tests/topotests/pim_acl/r12/zebra.conf
new file mode 100644
index 0000000000..bede104906
--- /dev/null
+++ b/tests/topotests/pim_acl/r12/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r12
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.12/32
+!
+interface r12-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.12/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/r13/acl_3_pim_join.json b/tests/topotests/pim_acl/r13/acl_3_pim_join.json
new file mode 100644
index 0000000000..48ad72cbe1
--- /dev/null
+++ b/tests/topotests/pim_acl/r13/acl_3_pim_join.json
@@ -0,0 +1,19 @@
+{
+ "r13-eth0":{
+ "name":"r13-eth0",
+ "state":"up",
+ "address":"192.168.101.13",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.32":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.32",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r13/ospfd.conf b/tests/topotests/pim_acl/r13/ospfd.conf
new file mode 100644
index 0000000000..830c5a14b6
--- /dev/null
+++ b/tests/topotests/pim_acl/r13/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r13
+!
+debug ospf event
+!
+interface r13-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 0
+!
+router ospf
+ ospf router-id 192.168.0.13
+ network 192.168.0.13/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_acl/r13/pimd.conf b/tests/topotests/pim_acl/r13/pimd.conf
new file mode 100644
index 0000000000..2ff1743574
--- /dev/null
+++ b/tests/topotests/pim_acl/r13/pimd.conf
@@ -0,0 +1,17 @@
+hostname r13
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.13 239.100.0.32/27
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r13-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_acl/r13/zebra.conf b/tests/topotests/pim_acl/r13/zebra.conf
new file mode 100644
index 0000000000..f9ff27abac
--- /dev/null
+++ b/tests/topotests/pim_acl/r13/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r13
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.13/32
+!
+interface r13-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.13/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/r14/acl_4_pim_join.json b/tests/topotests/pim_acl/r14/acl_4_pim_join.json
new file mode 100644
index 0000000000..46d86dd40d
--- /dev/null
+++ b/tests/topotests/pim_acl/r14/acl_4_pim_join.json
@@ -0,0 +1,19 @@
+{
+ "r14-eth0":{
+ "name":"r14-eth0",
+ "state":"up",
+ "address":"192.168.101.14",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.255":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.255",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r14/acl_5_pim_join.json b/tests/topotests/pim_acl/r14/acl_5_pim_join.json
new file mode 100644
index 0000000000..2b291a8a0c
--- /dev/null
+++ b/tests/topotests/pim_acl/r14/acl_5_pim_join.json
@@ -0,0 +1,19 @@
+{
+ "r14-eth0":{
+ "name":"r14-eth0",
+ "state":"up",
+ "address":"192.168.101.14",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.97":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.97",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r14/ospfd.conf b/tests/topotests/pim_acl/r14/ospfd.conf
new file mode 100644
index 0000000000..422e4c08b0
--- /dev/null
+++ b/tests/topotests/pim_acl/r14/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r14
+!
+debug ospf event
+!
+interface r14-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 0
+!
+router ospf
+ ospf router-id 192.168.0.14
+ network 192.168.0.14/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_acl/r14/pimd.conf b/tests/topotests/pim_acl/r14/pimd.conf
new file mode 100644
index 0000000000..1324a9e40b
--- /dev/null
+++ b/tests/topotests/pim_acl/r14/pimd.conf
@@ -0,0 +1,18 @@
+hostname r14
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.14 239.100.0.96/28
+ip pim rp 192.168.0.14 239.100.0.128/25
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r14-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_acl/r14/zebra.conf b/tests/topotests/pim_acl/r14/zebra.conf
new file mode 100644
index 0000000000..8761b46206
--- /dev/null
+++ b/tests/topotests/pim_acl/r14/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r14
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.14/32
+!
+interface r14-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.14/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/r15/acl_6_pim_join.json b/tests/topotests/pim_acl/r15/acl_6_pim_join.json
new file mode 100644
index 0000000000..05fed4ecc5
--- /dev/null
+++ b/tests/topotests/pim_acl/r15/acl_6_pim_join.json
@@ -0,0 +1,19 @@
+{
+ "r15-eth0":{
+ "name":"r15-eth0",
+ "state":"up",
+ "address":"192.168.101.15",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.70":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.70",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r15/ospfd.conf b/tests/topotests/pim_acl/r15/ospfd.conf
new file mode 100644
index 0000000000..cd4d7b3875
--- /dev/null
+++ b/tests/topotests/pim_acl/r15/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r15
+!
+debug ospf event
+!
+interface r15-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 0
+!
+router ospf
+ ospf router-id 192.168.0.15
+ network 192.168.0.15/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_acl/r15/pimd.conf b/tests/topotests/pim_acl/r15/pimd.conf
new file mode 100644
index 0000000000..f47e78c221
--- /dev/null
+++ b/tests/topotests/pim_acl/r15/pimd.conf
@@ -0,0 +1,17 @@
+hostname r15
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.15 239.100.0.64/28
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r15-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_acl/r15/zebra.conf b/tests/topotests/pim_acl/r15/zebra.conf
new file mode 100644
index 0000000000..f6909dd020
--- /dev/null
+++ b/tests/topotests/pim_acl/r15/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r15
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.15/32
+!
+interface r15-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.15/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/test_pim_acl.py b/tests/topotests/pim_acl/test_pim_acl.py
new file mode 100755
index 0000000000..848f7fa8ed
--- /dev/null
+++ b/tests/topotests/pim_acl/test_pim_acl.py
@@ -0,0 +1,418 @@
+#!/usr/bin/env python
+
+#
+# test_pim_acl.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# 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_pim_acl.py: Test PIM with RP selection using ACLs
+"""
+
+# Test PIM RP selection with ACLs
+#
+# Testing RP selection with ACLs. R1 uses multiple ACLs
+# to select desired RPs (R11 to R15)
+#
+# Test steps:
+# - setup_module()
+# Create topology. Hosts are only using zebra/staticd,
+# no PIM, no OSPF (using IGMPv2 for multicast)
+# - test_ospf_convergence()
+# Wait for OSPF convergence in each VRF. OSPF is run on
+# R1 and R11 - R15.
+# - test_pim_convergence()
+# Wait for PIM convergence on all routers. PIM is run on
+# R1 and R11 - R15.
+# - test_mcast_acl_1():
+# Test 1st ACL entry 239.100.0.0/28 with 239.100.0.1 which
+# should use R11 as RP
+# Stop multicast after verification
+# - test_mcast_acl_2():
+# Test 2nd ACL entry 239.100.0.17/32 with 239.100.0.17 which
+# should use R12 as RP
+# Stop multicast after verification
+# - test_mcast_acl_3():
+# Test 3rd ACL entry 239.100.0.32/27 with 239.100.0.32 which
+# should use R13 as RP
+# Stop multicast after verification
+# - test_mcast_acl_4():
+# Test 4th ACL entry 239.100.0.128/25 with 239.100.0.255 which
+# should use R14 as RP
+# Stop multicast after verification
+# - test_mcast_acl_5():
+# Test 5th ACL entry 239.100.0.96/28 with 239.100.0.97 which
+# should use R14 as RP
+# Stop multicast after verification
+# - test_mcast_acl_6():
+# Test 6th ACL entry 239.100.0.64/28 with 239.100.0.70 which
+# should use R15 as RP
+# Stop multicast after verification
+# - teardown_module()
+# shutdown topology
+#
+
+
+TOPOLOGY = """
+ +----------+
+ | Host H2 |
+ | Source |
+ +----------+
+ .2 |
+ +-----------+ | +----------+
+ | | .1 | .11 | Host R11 |
++---------+ | R1 |---------+--------| PIM RP |
+| Host H1 | 192.168.100.0/24 | | 192.168.101.0/24 +----------+
+| receive |------------------| uses ACLs | | +----------+
+|IGMP JOIN| .10 .1 | to pick | | .12 | Host R12 |
++---------+ | RP | +--------| PIM RP |
+ | | | +----------+
+ +-----------+ | +----------+
+ | .13 | Host R13 |
+ +--------| PIM RP |
+ | +----------+
+ | +----------+
+ | .14 | Host R14 |
+ +--------| PIM RP |
+ | +----------+
+ | +----------+
+ | .15 | Host R15 |
+ +--------| PIM RP |
+ +----------+
+"""
+
+import json
+import functools
+import os
+import sys
+import pytest
+import re
+import time
+from time import sleep
+import socket
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# 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
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+pytestmark = [pytest.mark.pimd]
+
+
+#
+# Test global variables:
+# They are used to handle communicating with external application.
+#
+APP_SOCK_PATH = '/tmp/topotests/apps.sock'
+HELPER_APP_PATH = os.path.join(CWD, "../lib/mcast-tester.py")
+app_listener = None
+app_clients = {}
+
+def listen_to_applications():
+ "Start listening socket to connect with applications."
+ # Remove old socket.
+ try:
+ os.unlink(APP_SOCK_PATH)
+ except OSError:
+ pass
+
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0)
+ sock.bind(APP_SOCK_PATH)
+ sock.listen(10)
+ global app_listener
+ app_listener = sock
+
+def accept_host(host):
+ "Accept connection from application running in hosts."
+ global app_listener, app_clients
+ conn = app_listener.accept()
+ app_clients[host] = {
+ 'fd': conn[0],
+ 'address': conn[1]
+ }
+
+def close_applications():
+ "Signal applications to stop and close all sockets."
+ global app_listener, app_clients
+
+ if app_listener:
+ # Close listening socket.
+ app_listener.close()
+
+ # Remove old socket.
+ try:
+ os.unlink(APP_SOCK_PATH)
+ except OSError:
+ pass
+
+ # Close all host connections.
+ for host in ["h1", "h2"]:
+ if app_clients.get(host) is None:
+ continue
+ app_clients[host]["fd"].close()
+
+ # Reset listener and clients data struct
+ app_listener = None
+ app_clients = {}
+
+
+class PIMACLTopo(Topo):
+ "PIM ACL Test Topology"
+
+ def build(self):
+ tgen = get_topogen(self)
+
+ # Create the hosts
+ for hostNum in range(1,3):
+ tgen.add_router("h{}".format(hostNum))
+
+ # Create the main router
+ tgen.add_router("r1")
+
+ # Create the PIM RP routers
+ for rtrNum in range(11, 16):
+ tgen.add_router("r{}".format(rtrNum))
+
+ # Setup Switches and connections
+ for swNum in range(1, 3):
+ tgen.add_switch("sw{}".format(swNum))
+
+ # Add connections H1 to R1 switch sw1
+ tgen.gears["h1"].add_link(tgen.gears["sw1"])
+ tgen.gears["r1"].add_link(tgen.gears["sw1"])
+
+ # Add connections R1 to R1x switch sw2
+ tgen.gears["r1"].add_link(tgen.gears["sw2"])
+ tgen.gears["h2"].add_link(tgen.gears["sw2"])
+ tgen.gears["r11"].add_link(tgen.gears["sw2"])
+ tgen.gears["r12"].add_link(tgen.gears["sw2"])
+ tgen.gears["r13"].add_link(tgen.gears["sw2"])
+ tgen.gears["r14"].add_link(tgen.gears["sw2"])
+ tgen.gears["r15"].add_link(tgen.gears["sw2"])
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+def setup_module(module):
+ logger.info("PIM RP ACL Topology: \n {}".format(TOPOLOGY))
+
+ tgen = Topogen(PIMACLTopo, module.__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_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ if rname[0] != 'h':
+ # Only load ospf on routers, not on end hosts
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_PIM, os.path.join(CWD, "{}/pimd.conf".format(rname))
+ )
+ tgen.start_router()
+
+
+def teardown_module(module):
+ tgen = get_topogen()
+ tgen.stop_topology()
+ close_applications()
+
+
+def test_ospf_convergence():
+ "Test for OSPFv2 convergence"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking OSPFv2 convergence on router r1")
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/ospf_neighbor.json")
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip ospf neighbor json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "OSPF router R1 did not converge"
+ assert res is None, assertmsg
+
+
+def test_pim_convergence():
+ "Test for PIM convergence"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking PIM convergence on router r1")
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/pim_neighbor.json")
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip pim neighbor json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "PIM router R1 did not converge"
+ assert res is None, assertmsg
+
+
+
+def check_mcast_entry(entry, mcastaddr, pimrp):
+ "Helper function to check RP"
+ tgen = get_topogen()
+
+ logger.info("Testing PIM RP selection for ACL {} entry using {}".format(entry, mcastaddr));
+
+ # Start applications socket.
+ listen_to_applications()
+
+ tgen.gears["h2"].run("{} --send='0.7' '{}' '{}' '{}' &".format(
+ HELPER_APP_PATH, APP_SOCK_PATH, mcastaddr, 'h2-eth0'))
+ accept_host("h2")
+
+ tgen.gears["h1"].run("{} '{}' '{}' '{}' &".format(
+ HELPER_APP_PATH, APP_SOCK_PATH, mcastaddr, 'h1-eth0'))
+ accept_host("h1")
+
+ logger.info("mcast join and source for {} started".format(mcastaddr))
+
+ # tgen.mininet_cli()
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/acl_{}_pim_join.json".format(entry))
+ expected = json.loads(open(reffile).read())
+
+ logger.info("verifying pim join on r1 for {}".format(mcastaddr))
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip pim join json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "PIM router r1 did not show join status"
+ assert res is None, assertmsg
+
+ logger.info("verifying pim join on PIM RP {} for {}".format(pimrp, mcastaddr))
+ router = tgen.gears[pimrp]
+ reffile = os.path.join(CWD, "{}/acl_{}_pim_join.json".format(pimrp, entry))
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip pim join json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "PIM router {} did not get selected as the PIM RP".format(pimrp)
+ assert res is None, assertmsg
+
+ close_applications()
+ return
+
+
+def test_mcast_acl_1():
+ "Test 1st ACL entry 239.100.0.0/28 with 239.100.0.1"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry(1, '239.100.0.1', 'r11')
+
+
+def test_mcast_acl_2():
+ "Test 2nd ACL entry 239.100.0.17/32 with 239.100.0.17"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry(2, '239.100.0.17', 'r12')
+
+
+def test_mcast_acl_3():
+ "Test 3rd ACL entry 239.100.0.32/27 with 239.100.0.32"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry(3, '239.100.0.32', 'r13')
+
+
+def test_mcast_acl_4():
+ "Test 4th ACL entry 239.100.0.128/25 with 239.100.0.255"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry(4, '239.100.0.255', 'r14')
+
+
+def test_mcast_acl_5():
+ "Test 5th ACL entry 239.100.0.96/28 with 239.100.0.97"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry(5, '239.100.0.97', 'r14')
+
+
+def test_mcast_acl_6():
+ "Test 6th ACL entry 239.100.0.64/28 with 239.100.0.70"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry(6, '239.100.0.70', 'r15')
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/pim_basic/r1/pimd.conf b/tests/topotests/pim_basic/r1/pimd.conf
index f64a46deb3..737019fa51 100644
--- a/tests/topotests/pim_basic/r1/pimd.conf
+++ b/tests/topotests/pim_basic/r1/pimd.conf
@@ -15,3 +15,4 @@ interface lo
ip pim
!
ip pim rp 10.254.0.3
+ip pim join-prune-interval 5
diff --git a/tests/topotests/pim_basic/rp/pimd.conf b/tests/topotests/pim_basic/rp/pimd.conf
index 6e35c97971..fd26bc4d71 100644
--- a/tests/topotests/pim_basic/rp/pimd.conf
+++ b/tests/topotests/pim_basic/rp/pimd.conf
@@ -6,6 +6,7 @@ interface rp-eth0
interface lo
ip pim
!
+ip pim join-prune-interval 5
ip pim rp 10.254.0.3
ip pim register-accept-list ACCEPT
diff --git a/tests/topotests/pim_basic_topo2/r2/pimd.conf b/tests/topotests/pim_basic_topo2/r2/pimd.conf
index 0b32ded19a..9f389deb11 100644
--- a/tests/topotests/pim_basic_topo2/r2/pimd.conf
+++ b/tests/topotests/pim_basic_topo2/r2/pimd.conf
@@ -10,3 +10,4 @@ interface r2-eth2
ip pim
ip pim bfd
!
+ip pim join-prune-interval 5
diff --git a/tests/topotests/pim_igmp_vrf/h1/zebra.conf b/tests/topotests/pim_igmp_vrf/h1/zebra.conf
new file mode 100644
index 0000000000..3d6540d40c
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/h1/zebra.conf
@@ -0,0 +1,10 @@
+!
+hostname h1
+log file zebra.log
+!
+interface h1-eth0
+ description connection to r1 via sw1
+ ip address 192.168.100.10/24
+!
+ip route 0.0.0.0/0 192.168.100.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/h2/zebra.conf b/tests/topotests/pim_igmp_vrf/h2/zebra.conf
new file mode 100644
index 0000000000..95342f9e8a
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/h2/zebra.conf
@@ -0,0 +1,8 @@
+hostname h2
+!
+interface h2-eth0
+ description connection to r1 via sw2
+ ip address 192.168.101.2/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/h3/zebra.conf b/tests/topotests/pim_igmp_vrf/h3/zebra.conf
new file mode 100644
index 0000000000..ef99b1cd8f
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/h3/zebra.conf
@@ -0,0 +1,10 @@
+!
+hostname h3
+log file zebra.log
+!
+interface h3-eth0
+ description connection to r1 via sw3
+ ip address 192.168.100.20/24
+!
+ip route 0.0.0.0/0 192.168.100.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/h4/zebra.conf b/tests/topotests/pim_igmp_vrf/h4/zebra.conf
new file mode 100644
index 0000000000..6a2e466000
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/h4/zebra.conf
@@ -0,0 +1,8 @@
+hostname h4
+!
+interface h4-eth0
+ description connection to r1 via sw4
+ ip address 192.168.101.4/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json
new file mode 100644
index 0000000000..604d25fac1
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json
@@ -0,0 +1,15 @@
+{
+ "blue":{
+ "vrfName":"blue",
+ "neighbors":{
+ "192.168.0.11":[
+ {
+ "priority":10,
+ "state":"Full\/Backup",
+ "address":"192.168.101.11",
+ "ifaceName":"r1-eth1:192.168.101.1"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json
new file mode 100644
index 0000000000..456bb87520
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json
@@ -0,0 +1,16 @@
+{
+ "red":{
+ "vrfName":"red",
+ "neighbors":{
+ "192.168.0.12":[
+ {
+ "priority":10,
+ "state":"Full\/Backup",
+ "address":"192.168.101.12",
+ "ifaceName":"r1-eth3:192.168.101.1"
+ }
+ ]
+ }
+ }
+}
+
diff --git a/tests/topotests/pim_igmp_vrf/r1/ospfd.conf b/tests/topotests/pim_igmp_vrf/r1/ospfd.conf
new file mode 100644
index 0000000000..263b5867cc
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/ospfd.conf
@@ -0,0 +1,26 @@
+hostname r1
+!
+debug ospf event
+!
+!
+interface r1-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 20
+!
+interface r1-eth3
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 20
+!
+router ospf vrf blue
+ ospf router-id 192.168.0.1
+ network 192.168.0.1/32 area 0
+ network 192.168.100.0/24 area 0
+ network 192.168.101.0/24 area 0
+router ospf vrf red
+ ospf router-id 192.168.0.1
+ network 192.168.0.1/32 area 0
+ network 192.168.100.0/24 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_blue_join.json b/tests/topotests/pim_igmp_vrf/r1/pim_blue_join.json
new file mode 100644
index 0000000000..8568bae2bc
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pim_blue_join.json
@@ -0,0 +1,22 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.1":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.1",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
+
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_blue_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/pim_blue_neighbor.json
new file mode 100644
index 0000000000..ea7d4aca6f
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pim_blue_neighbor.json
@@ -0,0 +1,13 @@
+{
+ "blue":{
+ },
+ "r1-eth0":{
+ },
+ "r1-eth1":{
+ "192.168.101.11":{
+ "interface":"r1-eth1",
+ "neighbor":"192.168.101.11",
+ "drPriority":1
+ }
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_blue_pimreg11.json b/tests/topotests/pim_igmp_vrf/r1/pim_blue_pimreg11.json
new file mode 100644
index 0000000000..d3642f854a
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pim_blue_pimreg11.json
@@ -0,0 +1,14 @@
+{
+ "pimreg11":{
+ "name":"pimreg11",
+ "state":"up",
+ "address":"0.0.0.0",
+ "flagAllMulticast":true,
+ "lanDelayEnabled":true,
+ "drAddress":"*",
+ "drPriority":1,
+ "drUptime":"--:--:--",
+ "drElections":0,
+ "drChanges":0
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_red_join.json b/tests/topotests/pim_igmp_vrf/r1/pim_red_join.json
new file mode 100644
index 0000000000..d0037ca4b0
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pim_red_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth2":{
+ "name":"r1-eth2",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.1":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.1",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_red_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/pim_red_neighbor.json
new file mode 100644
index 0000000000..e17b40854a
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pim_red_neighbor.json
@@ -0,0 +1,13 @@
+{
+ "r1-eth2":{
+ },
+ "r1-eth3":{
+ "192.168.101.12":{
+ "interface":"r1-eth3",
+ "neighbor":"192.168.101.12",
+ "drPriority":1
+ }
+ },
+ "red":{
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_red_pimreg12.json b/tests/topotests/pim_igmp_vrf/r1/pim_red_pimreg12.json
new file mode 100644
index 0000000000..45b6cd9645
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pim_red_pimreg12.json
@@ -0,0 +1,14 @@
+{
+ "pimreg12":{
+ "name":"pimreg12",
+ "state":"up",
+ "address":"0.0.0.0",
+ "flagAllMulticast":true,
+ "lanDelayEnabled":true,
+ "drAddress":"*",
+ "drPriority":1,
+ "drUptime":"--:--:--",
+ "drElections":0,
+ "drChanges":0
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/pimd.conf b/tests/topotests/pim_igmp_vrf/r1/pimd.conf
new file mode 100644
index 0000000000..f04c255de9
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pimd.conf
@@ -0,0 +1,27 @@
+hostname r1
+!
+debug igmp events
+debug igmp packets
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+interface r1-eth0
+ ip igmp
+ ip igmp version 2
+ ip pim
+!
+interface r1-eth1
+ ip pim
+!
+interface r1-eth2
+ ip igmp
+ ip igmp version 2
+ ip pim
+!
+interface r1-eth3
+ ip pim
+!
+ip pim join-prune-interval 5
diff --git a/tests/topotests/pim_igmp_vrf/r1/zebra.conf b/tests/topotests/pim_igmp_vrf/r1/zebra.conf
new file mode 100644
index 0000000000..9da9280945
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/zebra.conf
@@ -0,0 +1,30 @@
+!
+hostname r1
+log file zebra.log
+!
+ip forwarding
+ipv6 forwarding
+!
+interface blue vrf blue
+ ip address 192.168.0.1/32
+!
+interface red vrf red
+ ip address 192.168.0.1/32
+!
+interface r1-eth0 vrf blue
+ description connection to h1 via sw1
+ ip address 192.168.100.1/24
+!
+interface r1-eth1 vrf blue
+ description connection to r11 via sw2
+ ip address 192.168.101.1/24
+!
+interface r1-eth2 vrf red
+ description connection to h1 via sw3
+ ip address 192.168.100.1/24
+!
+interface r1-eth3 vrf red
+ description connection to r12 via sw4
+ ip address 192.168.101.1/24
+!
+
diff --git a/tests/topotests/pim_igmp_vrf/r11/ospfd.conf b/tests/topotests/pim_igmp_vrf/r11/ospfd.conf
new file mode 100644
index 0000000000..e107220a4e
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r11/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r11
+!
+debug ospf event
+!
+interface r11-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 10
+!
+router ospf
+ ospf router-id 192.168.0.11
+ network 192.168.0.11/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_igmp_vrf/r11/pim_blue_join.json b/tests/topotests/pim_igmp_vrf/r11/pim_blue_join.json
new file mode 100644
index 0000000000..289bf51e76
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r11/pim_blue_join.json
@@ -0,0 +1,19 @@
+{
+ "r11-eth0":{
+ "name":"r11-eth0",
+ "state":"up",
+ "address":"192.168.101.11",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.1":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.1",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r11/pimd.conf b/tests/topotests/pim_igmp_vrf/r11/pimd.conf
new file mode 100644
index 0000000000..b1d45205da
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r11/pimd.conf
@@ -0,0 +1,17 @@
+hostname r11
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.11 239.100.0.0/28
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r11-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_igmp_vrf/r11/zebra.conf b/tests/topotests/pim_igmp_vrf/r11/zebra.conf
new file mode 100644
index 0000000000..137706d245
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r11/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r11
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.11/32
+!
+interface r11-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.11/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/r12/ospfd.conf b/tests/topotests/pim_igmp_vrf/r12/ospfd.conf
new file mode 100644
index 0000000000..03acc82c1d
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r12/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r12
+!
+debug ospf event
+!
+interface r12-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 10
+!
+router ospf
+ ospf router-id 192.168.0.12
+ network 192.168.0.12/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_igmp_vrf/r12/pim_red_join.json b/tests/topotests/pim_igmp_vrf/r12/pim_red_join.json
new file mode 100644
index 0000000000..6926246568
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r12/pim_red_join.json
@@ -0,0 +1,19 @@
+{
+ "r12-eth0":{
+ "name":"r12-eth0",
+ "state":"up",
+ "address":"192.168.101.12",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.1":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.1",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r12/pimd.conf b/tests/topotests/pim_igmp_vrf/r12/pimd.conf
new file mode 100644
index 0000000000..5cb76efa22
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r12/pimd.conf
@@ -0,0 +1,17 @@
+hostname r12
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.12 239.100.0.0/28
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r12-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_igmp_vrf/r12/zebra.conf b/tests/topotests/pim_igmp_vrf/r12/zebra.conf
new file mode 100644
index 0000000000..bede104906
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r12/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r12
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.12/32
+!
+interface r12-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.12/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/test_pim_vrf.py b/tests/topotests/pim_igmp_vrf/test_pim_vrf.py
new file mode 100755
index 0000000000..298adef9c6
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/test_pim_vrf.py
@@ -0,0 +1,462 @@
+#!/usr/bin/env python
+
+#
+# test_pim_vrf.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# 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_pim_vrf.py: Test PIM with VRFs.
+"""
+
+# Tests PIM with VRF
+#
+# R1 is split into 2 VRF: Blue and Red, the others are normal
+# routers and Hosts
+# There are 2 similar topologies with overlapping IPs in each
+# section.
+#
+# Test steps:
+# - setup_module()
+# Create topology. Hosts are only using zebra/staticd,
+# no PIM, no OSPF (using IGMPv2 for multicast)
+# - test_ospf_convergence()
+# Wait for OSPF convergence in each VRF. OSPF is run on
+# R1, R11 and R12.
+# - test_pim_convergence()
+# Wait for PIM convergence in each VRF. PIM is run on
+# R1, R11 and R12. R11 is the RP for vrf blue, R12 is RP
+# for vrf red.
+# - test_vrf_pimreg_interfaces()
+# Adding PIM RP in VRF information and verify pimreg
+# interfaces in VRF blue and red
+# - test_mcast_vrf_blue()
+# Start multicast stream for group 239.100.0.1 from Host
+# H2 and join from Host H1 on vrf blue
+# Verify PIM JOIN status on R1 and R11
+# Stop multicast after verification
+# - test_mcast_vrf_red()
+# Start multicast stream for group 239.100.0.1 from Host
+# H4 and join from Host H3 on vrf blue
+# Verify PIM JOIN status on R1 and R12
+# Stop multicast after verification
+# - teardown_module(module)
+# shutdown topology
+#
+
+TOPOLOGY = """
+ +----------+
+ | Host H2 |
+ | Source |
+ +----------+
+ .2 |
++---------+ +------------+ | +---------+
+| Host H1 | 192.168.100.0/24 | | .1 | .11 | Host H2 |
+| receive |------------------| VRF Blue |---------+--------| PIM RP |
+|IGMP JOIN| .10 .1 | | 192.168.101.0/24 | |
++---------+ | | +---------+
+ =| = = R1 = = |=
++---------+ | | +---------+
+| Host H3 | 192.168.100.0/24 | | 192.168.101.0/24 | Host H4 |
+| receive |------------------| VRF Red |---------+--------| PIM RP |
+|IGMP JOIN| .20 .1 | | .1 | .12 | |
++---------+ +------------+ | +---------+
+ .4 |
+ +----------+
+ | Host H4 |
+ | Source |
+ +----------+
+"""
+
+import json
+import functools
+import os
+import sys
+import pytest
+import re
+import time
+from time import sleep
+import socket
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# 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.topotest import iproute2_is_vrf_capable
+from lib.common_config import (
+ required_linux_kernel_version)
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+pytestmark = [pytest.mark.pimd]
+
+
+#
+# Test global variables:
+# They are used to handle communicating with external application.
+#
+APP_SOCK_PATH = '/tmp/topotests/apps.sock'
+HELPER_APP_PATH = os.path.join(CWD, "../lib/mcast-tester.py")
+app_listener = None
+app_clients = {}
+
+def listen_to_applications():
+ "Start listening socket to connect with applications."
+ # Remove old socket.
+ try:
+ os.unlink(APP_SOCK_PATH)
+ except OSError:
+ pass
+
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0)
+ sock.bind(APP_SOCK_PATH)
+ sock.listen(10)
+ global app_listener
+ app_listener = sock
+
+def accept_host(host):
+ "Accept connection from application running in hosts."
+ global app_listener, app_clients
+ conn = app_listener.accept()
+ app_clients[host] = {
+ 'fd': conn[0],
+ 'address': conn[1]
+ }
+
+def close_applications():
+ "Signal applications to stop and close all sockets."
+ global app_listener, app_clients
+
+ if app_listener:
+ # Close listening socket.
+ app_listener.close()
+
+ # Remove old socket.
+ try:
+ os.unlink(APP_SOCK_PATH)
+ except OSError:
+ pass
+
+ # Close all host connections.
+ for host in ["h1", "h2"]:
+ if app_clients.get(host) is None:
+ continue
+ app_clients[host]["fd"].close()
+
+ # Reset listener and clients data struct
+ app_listener = None
+ app_clients = {}
+
+
+class PIMVRFTopo(Topo):
+ "PIM VRF Test Topology"
+
+ def build(self):
+ tgen = get_topogen(self)
+
+ # Create the hosts
+ for hostNum in range(1,5):
+ tgen.add_router("h{}".format(hostNum))
+
+ # Create the main router
+ tgen.add_router("r1")
+
+ # Create the PIM RP routers
+ for rtrNum in range(11, 13):
+ tgen.add_router("r{}".format(rtrNum))
+
+ # Setup Switches and connections
+ for swNum in range(1, 5):
+ tgen.add_switch("sw{}".format(swNum))
+
+ ################
+ # 1st set of connections to routers for VRF red
+ ################
+
+ # Add connections H1 to R1 switch sw1
+ tgen.gears["h1"].add_link(tgen.gears["sw1"])
+ tgen.gears["r1"].add_link(tgen.gears["sw1"])
+
+ # Add connections R1 to R1x switch sw2
+ tgen.gears["r1"].add_link(tgen.gears["sw2"])
+ tgen.gears["h2"].add_link(tgen.gears["sw2"])
+ tgen.gears["r11"].add_link(tgen.gears["sw2"])
+
+ ################
+ # 2nd set of connections to routers for vrf blue
+ ################
+
+ # Add connections H1 to R1 switch sw1
+ tgen.gears["h3"].add_link(tgen.gears["sw3"])
+ tgen.gears["r1"].add_link(tgen.gears["sw3"])
+
+ # Add connections R1 to R1x switch sw2
+ tgen.gears["r1"].add_link(tgen.gears["sw4"])
+ tgen.gears["h4"].add_link(tgen.gears["sw4"])
+ tgen.gears["r12"].add_link(tgen.gears["sw4"])
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+def setup_module(module):
+ logger.info("PIM IGMP VRF Topology: \n {}".format(TOPOLOGY))
+
+ tgen = Topogen(PIMVRFTopo, module.__name__)
+ tgen.start_topology()
+
+ vrf_setup_cmds = [
+ "ip link add name blue type vrf table 11",
+ "ip link add name red type vrf table 12",
+ "ip link set dev blue up",
+ "ip link set dev red up",
+ "ip link set dev r1-eth0 vrf blue up",
+ "ip link set dev r1-eth1 vrf blue up",
+ "ip link set dev r1-eth2 vrf red up",
+ "ip link set dev r1-eth3 vrf red up",
+ ]
+
+ # Starting Routers
+ router_list = tgen.routers()
+
+ # Create VRF on r2 first and add it's interfaces
+ for cmd in vrf_setup_cmds:
+ tgen.net["r1"].cmd(cmd)
+
+ for rname, router in router_list.items():
+ logger.info("Loading router %s" % rname)
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ if rname[0] != 'h':
+ # Only load ospf on routers, not on end hosts
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_PIM, os.path.join(CWD, "{}/pimd.conf".format(rname))
+ )
+ tgen.start_router()
+
+
+def teardown_module(module):
+ tgen = get_topogen()
+ tgen.stop_topology()
+ close_applications()
+
+
+def test_ospf_convergence():
+ "Test for OSPFv2 convergence"
+ tgen = get_topogen()
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ # iproute2 needs to support VRFs for this suite to run.
+ if not iproute2_is_vrf_capable():
+ pytest.skip("Installed iproute2 version does not support VRFs")
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking OSPFv2 convergence on router r1 for VRF blue")
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/ospf_blue_neighbor.json")
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip ospf vrf blue neighbor json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "OSPF router R1 did not converge on VRF blue"
+ assert res is None, assertmsg
+
+ logger.info("Checking OSPFv2 convergence on router r1 for VRF red")
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/ospf_red_neighbor.json")
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip ospf vrf red neighbor json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "OSPF router R1 did not converge on VRF red"
+ assert res is None, assertmsg
+
+
+def test_pim_convergence():
+ "Test for PIM convergence"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking PIM convergence on router r1 for VRF red")
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/pim_red_neighbor.json")
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip pim vrf red neighbor json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=30, wait=2)
+ assertmsg = "PIM router R1 did not converge for VRF red"
+ assert res is None, assertmsg
+
+ logger.info("Checking PIM convergence on router r1 for VRF blue")
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/pim_blue_neighbor.json")
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip pim vrf blue neighbor json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=30, wait=2)
+ assertmsg = "PIM router R1 did not converge for VRF blue"
+ assert res is None, assertmsg
+
+
+def test_vrf_pimreg_interfaces():
+ "Adding PIM RP in VRF information and verify pimreg interfaces"
+ tgen = get_topogen()
+
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("conf\ninterface blue\nip pim")
+ r1.vtysh_cmd("conf\nvrf blue\nip pim rp 192.168.0.11 239.100.0.1/32\nexit-vrf")
+
+ # Check pimreg11 interface on R1, VRF blue
+ reffile = os.path.join(CWD, "r1/pim_blue_pimreg11.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, r1, "show ip pim vrf blue inter pimreg11 json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=5, wait=2)
+ assertmsg = "PIM router R1, VRF blue (table 11) pimreg11 interface missing or incorrect status"
+ assert res is None, assertmsg
+
+ r1.vtysh_cmd("conf\ninterface red\nip pim")
+ r1.vtysh_cmd("conf\nvrf red\nip pim rp 192.168.0.12 239.100.0.1/32\nexit-vrf")
+
+ # Check pimreg12 interface on R1, VRF red
+ reffile = os.path.join(CWD, "r1/pim_red_pimreg12.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, r1, "show ip pim vrf red inter pimreg12 json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=5, wait=2)
+ assertmsg = "PIM router R1, VRF red (table 12) pimreg12 interface missing or incorrect status"
+ assert res is None, assertmsg
+
+
+##################################
+### Test PIM / IGMP with VRF
+##################################
+
+def check_mcast_entry(mcastaddr, pimrp, receiver, sender, vrf):
+ "Helper function to check RP"
+ tgen = get_topogen()
+
+ logger.info("Testing PIM for VRF {} entry using {}".format(vrf, mcastaddr));
+
+ # Start applications socket.
+ listen_to_applications()
+
+ tgen.gears[sender].run("{} --send='0.7' '{}' '{}' '{}' &".format(
+ HELPER_APP_PATH, APP_SOCK_PATH, mcastaddr, '{}-eth0'.format(sender)))
+ accept_host(sender)
+
+ tgen.gears[receiver].run("{} '{}' '{}' '{}' &".format(
+ HELPER_APP_PATH, APP_SOCK_PATH, mcastaddr, '{}-eth0'.format(receiver)))
+ accept_host(receiver)
+
+ logger.info("mcast join and source for {} started".format(mcastaddr))
+
+ # tgen.mininet_cli()
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/pim_{}_join.json".format(vrf))
+ expected = json.loads(open(reffile).read())
+
+ logger.info("verifying pim join on r1 for {} on VRF {}".format(mcastaddr, vrf))
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip pim vrf {} join json".format(vrf),
+ expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=2)
+ assertmsg = "PIM router r1 did not show join status on VRF".format(vrf)
+ assert res is None, assertmsg
+
+ logger.info("verifying pim join on PIM RP {} for {}".format(pimrp, mcastaddr))
+ router = tgen.gears[pimrp]
+ reffile = os.path.join(CWD, "{}/pim_{}_join.json".format(pimrp, vrf))
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip pim join json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=2)
+ assertmsg = "PIM router {} did not get selected as the PIM RP for VRF {}".format(pimrp, vrf)
+ assert res is None, assertmsg
+
+ close_applications()
+ return
+
+
+def test_mcast_vrf_blue():
+ "Test vrf blue with 239.100.0.1"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry('239.100.0.1', 'r11', 'h1', 'h2', 'blue')
+
+
+def test_mcast_vrf_red():
+ "Test vrf red with 239.100.0.1"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry('239.100.0.1', 'r12', 'h3', 'h4', 'red')
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tools/coccinelle/hash_compare_null_values_check.cocci b/tools/coccinelle/hash_compare_null_values_check.cocci
new file mode 100644
index 0000000000..38649a2282
--- /dev/null
+++ b/tools/coccinelle/hash_compare_null_values_check.cocci
@@ -0,0 +1,20 @@
+// There is no need to test for null values in the hash compare
+// function as that we are guaranteed to send in data in
+// the hash compare functions.
+@@
+identifier fn =~ "_hash_cmp";
+type T;
+identifier p1;
+identifier p2;
+@@
+
+?static
+T fn(...)
+{
+...
+- if (p1 == NULL && p2 == NULL)
+- return ...;
+- if (p1 == NULL || p2 == NULL)
+- return ...;
+...
+}
diff --git a/tools/frr-reload.py b/tools/frr-reload.py
index eb8753fd08..9d41305ec3 100755
--- a/tools/frr-reload.py
+++ b/tools/frr-reload.py
@@ -2012,6 +2012,11 @@ if __name__ == "__main__":
parser.add_argument(
"--daemon", help="daemon for which want to replace the config", default=""
)
+ parser.add_argument(
+ "--test-reset",
+ action="store_true",
+ help="Used by topotest to not delete debug or log file commands",
+ )
args = parser.parse_args()
@@ -2125,7 +2130,7 @@ if __name__ == "__main__":
service_integrated_vtysh_config = False
break
- if not service_integrated_vtysh_config and not args.daemon:
+ if not args.test and not service_integrated_vtysh_config and not args.daemon:
log.error(
"'service integrated-vtysh-config' is not configured, this is required for 'service frr reload'"
)
@@ -2153,35 +2158,56 @@ if __name__ == "__main__":
running.load_from_show_running(args.daemon)
(lines_to_add, lines_to_del) = compare_context_objects(newconf, running)
- lines_to_configure = []
if lines_to_del:
- print("\nLines To Delete")
- print("===============")
+ if not args.test_reset:
+ print("\nLines To Delete")
+ print("===============")
for (ctx_keys, line) in lines_to_del:
if line == "!":
continue
- cmd = "\n".join(lines_to_config(ctx_keys, line, True))
- lines_to_configure.append(cmd)
+ nolines = lines_to_config(ctx_keys, line, True)
+
+ if args.test_reset:
+ # For topotests the original code stripped the lines, and ommitted blank lines
+ # after, do that here
+ nolines = [x.strip() for x in nolines]
+ # For topotests leave these lines in (don't delete them)
+ # [chopps: why is "log file" more special than other "log" commands?]
+ nolines = [x for x in nolines if "debug" not in x and "log file" not in x]
+ if not nolines:
+ continue
+
+ cmd = "\n".join(nolines)
print(cmd)
if lines_to_add:
- print("\nLines To Add")
- print("============")
+ if not args.test_reset:
+ print("\nLines To Add")
+ print("============")
for (ctx_keys, line) in lines_to_add:
if line == "!":
continue
- cmd = "\n".join(lines_to_config(ctx_keys, line, False))
- lines_to_configure.append(cmd)
+ lines = lines_to_config(ctx_keys, line, False)
+
+ if args.test_reset:
+ # For topotests the original code stripped the lines, and ommitted blank lines
+ # after, do that here
+ lines = [x.strip() for x in lines if x.strip()]
+ if not lines:
+ continue
+
+ cmd = "\n".join(lines)
print(cmd)
elif args.reload:
+ lines_to_configure = []
# We will not be able to do anything, go ahead and exit(1)
if not vtysh.is_config_available():
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index dd3f448674..5cee0aaa3f 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -478,14 +478,6 @@ static int vtysh_execute_func(const char *line, int pager)
if (vline == NULL)
return CMD_SUCCESS;
- if (user_mode) {
- if (strncmp("en", vector_slot(vline, 0), 2) == 0) {
- cmd_free_strvec(vline);
- vty_out(vty, "%% Command not allowed: enable\n");
- return CMD_WARNING;
- }
- }
-
if (vtysh_add_timestamp && strncmp(line, "exit", 4)) {
char ts[48];
@@ -2803,6 +2795,18 @@ DEFUNSH(VTYSH_INTERFACE, vtysh_quit_interface, vtysh_quit_interface_cmd, "quit",
return vtysh_exit_interface(self, vty, argc, argv);
}
+DEFUNSH(VTYSH_ZEBRA, vtysh_exit_pseudowire, vtysh_exit_pseudowire_cmd, "exit",
+ "Exit current mode and down to previous mode\n")
+{
+ return vtysh_exit(vty);
+}
+
+DEFUNSH(VTYSH_ZEBRA, vtysh_quit_pseudowire, vtysh_quit_pseudowire_cmd, "quit",
+ "Exit current mode and down to previous mode\n")
+{
+ return vtysh_exit_pseudowire(self, vty, argc, argv);
+}
+
static char *do_prepend(struct vty *vty, struct cmd_token **argv, int argc)
{
const char *argstr[argc + 1];
@@ -2907,6 +2911,20 @@ DEFUNSH(VTYSH_ZEBRA, exit_link_params, exit_link_params_cmd, "exit-link-params",
return CMD_SUCCESS;
}
+DEFUNSH(VTYSH_ZEBRA, vtysh_exit_link_params, vtysh_exit_link_params_cmd, "exit",
+ "Exit current mode and down to previous mode\n")
+{
+ if (vty->node == LINK_PARAMS_NODE)
+ vty->node = INTERFACE_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUNSH(VTYSH_ZEBRA, vtysh_quit_link_params, vtysh_quit_link_params_cmd, "quit",
+ "Exit current mode and down to previous mode\n")
+{
+ return vtysh_exit_link_params(self, vty, argc, argv);
+}
+
DEFUNSH_HIDDEN (0x00,
vtysh_debug_all,
vtysh_debug_all_cmd,
@@ -4445,13 +4463,14 @@ void vtysh_init_vty(void)
install_element(INTERFACE_NODE, &vtysh_link_params_cmd);
install_element(LINK_PARAMS_NODE, &exit_link_params_cmd);
install_element(LINK_PARAMS_NODE, &vtysh_end_all_cmd);
- install_element(LINK_PARAMS_NODE, &vtysh_exit_interface_cmd);
+ install_element(LINK_PARAMS_NODE, &vtysh_exit_link_params_cmd);
+ install_element(LINK_PARAMS_NODE, &vtysh_quit_link_params_cmd);
install_node(&pw_node);
install_element(CONFIG_NODE, &vtysh_pseudowire_cmd);
install_element(PW_NODE, &vtysh_end_all_cmd);
- install_element(PW_NODE, &vtysh_exit_interface_cmd);
- install_element(PW_NODE, &vtysh_quit_interface_cmd);
+ install_element(PW_NODE, &vtysh_exit_pseudowire_cmd);
+ install_element(PW_NODE, &vtysh_quit_pseudowire_cmd);
install_node(&vrf_node);
install_element(CONFIG_NODE, &vtysh_vrf_cmd);
@@ -4485,7 +4504,8 @@ void vtysh_init_vty(void)
/* vtysh */
- install_element(VIEW_NODE, &vtysh_enable_cmd);
+ if (!user_mode)
+ install_element(VIEW_NODE, &vtysh_enable_cmd);
install_element(ENABLE_NODE, &vtysh_config_terminal_cmd);
install_element(ENABLE_NODE, &vtysh_disable_cmd);
diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang
index 1c990b5ed9..e11883a803 100644
--- a/yang/frr-bgp-route-map.yang
+++ b/yang/frr-bgp-route-map.yang
@@ -36,6 +36,12 @@ module frr-bgp-route-map {
"Initial revision";
}
+ identity match-alias {
+ base frr-route-map:rmap-match-type;
+ description
+ "Match BGP community alias name";
+ }
+
identity match-local-preference {
base frr-route-map:rmap-match-type;
description
@@ -352,7 +358,14 @@ module frr-bgp-route-map {
}
}
- case script {
+ case alias {
+ when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'frr-bgp-route-map:match-alias')";
+ leaf alias {
+ type string;
+ }
+ }
+
+ case script {
when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'frr-bgp-route-map:match-script')";
leaf script {
type string;
diff --git a/yang/frr-pim.yang b/yang/frr-pim.yang
index 6329e45588..e846ffa1f8 100644
--- a/yang/frr-pim.yang
+++ b/yang/frr-pim.yang
@@ -351,8 +351,8 @@ module frr-pim {
}
leaf hello-holdtime {
- type uint8 {
- range "1..180";
+ type uint16 {
+ range "1..630";
}
must ". > ./../hello-interval" {
error-message "HoldTime must be greater than Hello";
@@ -529,7 +529,7 @@ module frr-pim {
}
leaf join-prune-interval {
type uint16 {
- range "60..600";
+ range "5..600";
}
default "60";
description
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index c2b4dcc52f..a51e0b82cb 100644
--- a/zebra/if_netlink.c
+++ b/zebra/if_netlink.c
@@ -706,6 +706,9 @@ static int netlink_bridge_vxlan_update(struct interface *ifp,
struct bridge_vlan_info *vinfo;
vlanid_t access_vlan;
+ if (!af_spec)
+ return 0;
+
/* There is a 1-to-1 mapping of VLAN to VxLAN - hence
* only 1 access VLAN is accepted.
*/
@@ -742,23 +745,26 @@ static void netlink_bridge_vlan_update(struct interface *ifp,
/* create a new bitmap space for re-eval */
bf_init(zif->vlan_bitmap, IF_VLAN_BITMAP_MAX);
- for (i = RTA_DATA(af_spec), rem = RTA_PAYLOAD(af_spec);
- RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
+ if (af_spec) {
+ for (i = RTA_DATA(af_spec), rem = RTA_PAYLOAD(af_spec);
+ RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
- if (i->rta_type != IFLA_BRIDGE_VLAN_INFO)
- continue;
+ if (i->rta_type != IFLA_BRIDGE_VLAN_INFO)
+ continue;
- vinfo = RTA_DATA(i);
+ vinfo = RTA_DATA(i);
- if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
- vid_range_start = vinfo->vid;
- continue;
- }
+ if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
+ vid_range_start = vinfo->vid;
+ continue;
+ }
- if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END))
- vid_range_start = vinfo->vid;
+ if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END))
+ vid_range_start = vinfo->vid;
- zebra_vlan_bitmap_compute(ifp, vid_range_start, vinfo->vid);
+ zebra_vlan_bitmap_compute(ifp, vid_range_start,
+ vinfo->vid);
+ }
}
zebra_vlan_mbr_re_eval(ifp, old_vlan_bitmap);
@@ -794,8 +800,6 @@ static int netlink_bridge_interface(struct nlmsghdr *h, int len, ns_id_t ns_id,
/* We are only interested in the access VLAN i.e., AF_SPEC */
af_spec = tb[IFLA_AF_SPEC];
- if (!af_spec)
- return 0;
if (IS_ZEBRA_IF_VXLAN(ifp))
return netlink_bridge_vxlan_update(ifp, af_spec);
diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c
index 252bf04782..5c060ac6f8 100644
--- a/zebra/kernel_socket.c
+++ b/zebra/kernel_socket.c
@@ -849,8 +849,8 @@ int ifam_read(struct ifa_msghdr *ifam)
bool dest_same = false;
char ifname[INTERFACE_NAMSIZ];
short ifnlen = 0;
- char isalias = 0;
- int flags = 0;
+ bool isalias = false;
+ uint32_t flags = 0;
ifname[0] = ifname[INTERFACE_NAMSIZ - 1] = '\0';
@@ -865,7 +865,13 @@ int ifam_read(struct ifa_msghdr *ifam)
}
if (ifnlen && strncmp(ifp->name, ifname, INTERFACE_NAMSIZ))
- isalias = 1;
+ isalias = true;
+
+ /*
+ * Mark the alias prefixes as secondary
+ */
+ if (isalias)
+ SET_FLAG(flags, ZEBRA_IFA_SECONDARY);
/* N.B. The info in ifa_msghdr does not tell us whether the RTA_BRD
field contains a broadcast address or a peer address, so we are
diff --git a/zebra/redistribute.c b/zebra/redistribute.c
index 89f46f9c97..26f6d404e9 100644
--- a/zebra/redistribute.c
+++ b/zebra/redistribute.c
@@ -153,10 +153,16 @@ static bool zebra_redistribute_check(const struct route_entry *re,
struct zserv *client,
const struct prefix *p, int afi)
{
+ struct zebra_vrf *zvrf;
+
/* Process only if there is valid re */
if (!re)
return false;
+ zvrf = vrf_info_lookup(re->vrf_id);
+ if (re->vrf_id == VRF_DEFAULT && zvrf->table_id != re->table)
+ return false;
+
/* If default route and redistributed */
if (is_default_prefix(p)
&& vrf_bitmap_check(client->redist_default[afi], re->vrf_id))
diff --git a/zebra/rib.h b/zebra/rib.h
index b7ffb9ce8d..31d9dfd265 100644
--- a/zebra/rib.h
+++ b/zebra/rib.h
@@ -177,15 +177,16 @@ struct route_entry {
/* meta-queue structure:
* sub-queue 0: nexthop group objects
- * sub-queue 1: connected
- * sub-queue 2: kernel
- * sub-queue 3: static
- * sub-queue 4: RIP, RIPng, OSPF, OSPF6, IS-IS, EIGRP, NHRP
- * sub-queue 5: iBGP, eBGP
- * sub-queue 6: any other origin (if any) typically those that
+ * sub-queue 1: EVPN/VxLAN objects
+ * sub-queue 2: connected
+ * sub-queue 3: kernel
+ * sub-queue 4: static
+ * sub-queue 5: RIP, RIPng, OSPF, OSPF6, IS-IS, EIGRP, NHRP
+ * sub-queue 6: iBGP, eBGP
+ * sub-queue 7: any other origin (if any) typically those that
* don't generate routes
*/
-#define MQ_SIZE 7
+#define MQ_SIZE 8
struct meta_queue {
struct list *subq[MQ_SIZE];
uint32_t size; /* sum of lengths of all subqueues */
@@ -446,6 +447,36 @@ extern int rib_queue_nhg_ctx_add(struct nhg_ctx *ctx);
/* Enqueue incoming nhg from proto daemon for processing */
extern int rib_queue_nhe_add(struct nhg_hash_entry *nhe);
+/* Enqueue evpn route for processing */
+int zebra_rib_queue_evpn_route_add(vrf_id_t vrf_id, const struct ethaddr *rmac,
+ const struct ipaddr *vtep_ip,
+ const struct prefix *host_prefix);
+int zebra_rib_queue_evpn_route_del(vrf_id_t vrf_id,
+ const struct ipaddr *vtep_ip,
+ const struct prefix *host_prefix);
+/* Enqueue EVPN remote ES for processing */
+int zebra_rib_queue_evpn_rem_es_add(const esi_t *esi,
+ const struct in_addr *vtep_ip,
+ bool esr_rxed, uint8_t df_alg,
+ uint16_t df_pref);
+int zebra_rib_queue_evpn_rem_es_del(const esi_t *esi,
+ const struct in_addr *vtep_ip);
+/* Enqueue EVPN remote macip update for processing */
+int zebra_rib_queue_evpn_rem_macip_del(vni_t vni, const struct ethaddr *macaddr,
+ const struct ipaddr *ip,
+ struct in_addr vtep_ip);
+int zebra_rib_queue_evpn_rem_macip_add(vni_t vni, const struct ethaddr *macaddr,
+ const struct ipaddr *ipaddr,
+ uint8_t flags, uint32_t seq,
+ struct in_addr vtep_ip,
+ const esi_t *esi);
+/* Enqueue VXLAN remote vtep update for processing */
+int zebra_rib_queue_evpn_rem_vtep_add(vrf_id_t vrf_id, vni_t vni,
+ struct in_addr vtep_ip,
+ int flood_control);
+int zebra_rib_queue_evpn_rem_vtep_del(vrf_id_t vrf_id, vni_t vni,
+ struct in_addr vtep_ip);
+
extern void meta_queue_free(struct meta_queue *mq);
extern int zebra_rib_labeled_unicast(struct route_entry *re);
extern struct route_table *rib_table_ipv6;
diff --git a/zebra/rt.h b/zebra/rt.h
index f79ddbe958..929a44ade7 100644
--- a/zebra/rt.h
+++ b/zebra/rt.h
@@ -93,10 +93,10 @@ extern void macfdb_read_for_bridge(struct zebra_ns *zns, struct interface *ifp,
struct interface *br_if);
extern void macfdb_read_specific_mac(struct zebra_ns *zns,
struct interface *br_if,
- struct ethaddr *mac, vlanid_t vid);
+ const struct ethaddr *mac, vlanid_t vid);
extern void neigh_read(struct zebra_ns *zns);
extern void neigh_read_for_vlan(struct zebra_ns *zns, struct interface *ifp);
-extern void neigh_read_specific_ip(struct ipaddr *ip,
+extern void neigh_read_specific_ip(const struct ipaddr *ip,
struct interface *vlan_if);
extern void route_read(struct zebra_ns *zns);
extern int kernel_upd_mac_nh(uint32_t nh_id, struct in_addr vtep_ip);
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index 38f8140db2..a64ec52dda 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -3466,10 +3466,9 @@ int netlink_macfdb_read_for_bridge(struct zebra_ns *zns, struct interface *ifp,
/* Request for MAC FDB for a specific MAC address in VLAN from the kernel */
static int netlink_request_specific_mac_in_bridge(struct zebra_ns *zns,
- int family,
- int type,
+ int family, int type,
struct interface *br_if,
- struct ethaddr *mac,
+ const struct ethaddr *mac,
vlanid_t vid)
{
struct {
@@ -3506,7 +3505,7 @@ static int netlink_request_specific_mac_in_bridge(struct zebra_ns *zns,
int netlink_macfdb_read_specific_mac(struct zebra_ns *zns,
struct interface *br_if,
- struct ethaddr *mac, vlanid_t vid)
+ const struct ethaddr *mac, vlanid_t vid)
{
int ret = 0;
struct zebra_dplane_info dp_info;
@@ -3946,7 +3945,8 @@ int netlink_neigh_read_for_vlan(struct zebra_ns *zns, struct interface *vlan_if)
* read using netlink interface.
*/
static int netlink_request_specific_neigh_in_vlan(struct zebra_ns *zns,
- int type, struct ipaddr *ip,
+ int type,
+ const struct ipaddr *ip,
ifindex_t ifindex)
{
struct {
@@ -3983,8 +3983,8 @@ static int netlink_request_specific_neigh_in_vlan(struct zebra_ns *zns,
return netlink_request(&zns->netlink_cmd, &req);
}
-int netlink_neigh_read_specific_ip(struct ipaddr *ip,
- struct interface *vlan_if)
+int netlink_neigh_read_specific_ip(const struct ipaddr *ip,
+ struct interface *vlan_if)
{
int ret = 0;
struct zebra_ns *zns;
diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h
index 4e41ff984b..93c06e555b 100644
--- a/zebra/rt_netlink.h
+++ b/zebra/rt_netlink.h
@@ -99,8 +99,9 @@ extern int netlink_neigh_read_for_vlan(struct zebra_ns *zns,
struct interface *vlan_if);
extern int netlink_macfdb_read_specific_mac(struct zebra_ns *zns,
struct interface *br_if,
- struct ethaddr *mac, uint16_t vid);
-extern int netlink_neigh_read_specific_ip(struct ipaddr *ip,
+ const struct ethaddr *mac,
+ uint16_t vid);
+extern int netlink_neigh_read_specific_ip(const struct ipaddr *ip,
struct interface *vlan_if);
extern vrf_id_t vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id);
diff --git a/zebra/rtread_netlink.c b/zebra/rtread_netlink.c
index fbca47351d..f70b006acd 100644
--- a/zebra/rtread_netlink.c
+++ b/zebra/rtread_netlink.c
@@ -46,9 +46,9 @@ void macfdb_read_for_bridge(struct zebra_ns *zns, struct interface *ifp,
}
void macfdb_read_specific_mac(struct zebra_ns *zns, struct interface *br_if,
- struct ethaddr *mac, vlanid_t vid)
+ const struct ethaddr *mac, vlanid_t vid)
{
-netlink_macfdb_read_specific_mac(zns, br_if, mac, vid);
+ netlink_macfdb_read_specific_mac(zns, br_if, mac, vid);
}
void neigh_read(struct zebra_ns *zns)
@@ -61,7 +61,7 @@ void neigh_read_for_vlan(struct zebra_ns *zns, struct interface *vlan_if)
netlink_neigh_read_for_vlan(zns, vlan_if);
}
-void neigh_read_specific_ip(struct ipaddr *ip, struct interface *vlan_if)
+void neigh_read_specific_ip(const struct ipaddr *ip, struct interface *vlan_if)
{
netlink_neigh_read_specific_ip(ip, vlan_if);
}
diff --git a/zebra/rtread_sysctl.c b/zebra/rtread_sysctl.c
index 74c6825ba1..594f7c2dd9 100644
--- a/zebra/rtread_sysctl.c
+++ b/zebra/rtread_sysctl.c
@@ -88,7 +88,7 @@ void macfdb_read_for_bridge(struct zebra_ns *zns, struct interface *ifp,
}
void macfdb_read_specific_mac(struct zebra_ns *zns, struct interface *br_if,
- struct ethaddr *mac, vlanid_t vid)
+ const struct ethaddr *mac, vlanid_t vid)
{
}
@@ -100,7 +100,7 @@ void neigh_read_for_vlan(struct zebra_ns *zns, struct interface *vlan_if)
{
}
-void neigh_read_specific_ip(struct ipaddr *ip, struct interface *vlan_if)
+void neigh_read_specific_ip(const struct ipaddr *ip, struct interface *vlan_if)
{
}
diff --git a/zebra/subdir.am b/zebra/subdir.am
index 70d8c4005d..731f0c9ad1 100644
--- a/zebra/subdir.am
+++ b/zebra/subdir.am
@@ -196,14 +196,14 @@ zebra_zebra_irdp_la_SOURCES = \
zebra/irdp_main.c \
zebra/irdp_packet.c \
# end
-zebra_zebra_irdp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+zebra_zebra_irdp_la_LDFLAGS = $(MODULE_LDFLAGS)
zebra_zebra_snmp_la_SOURCES = zebra/zebra_snmp.c
zebra_zebra_snmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
-zebra_zebra_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+zebra_zebra_snmp_la_LDFLAGS = $(MODULE_LDFLAGS)
zebra_zebra_snmp_la_LIBADD = lib/libfrrsnmp.la
-zebra_zebra_fpm_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+zebra_zebra_fpm_la_LDFLAGS = $(MODULE_LDFLAGS)
zebra_zebra_fpm_la_LIBADD =
zebra_zebra_fpm_la_SOURCES = zebra/zebra_fpm.c
zebra_zebra_fpm_la_SOURCES += zebra/zebra_fpm_netlink.c
@@ -220,7 +220,7 @@ endif
# Sample dataplane plugin
if DEV_BUILD
zebra_dplane_sample_plugin_la_SOURCES = zebra/sample_plugin.c
-zebra_dplane_sample_plugin_la_LDFLAGS = -module -shared -avoid-version -export-dynamic
+zebra_dplane_sample_plugin_la_LDFLAGS = $(MODULE_LDFLAGS)
endif
nodist_zebra_zebra_SOURCES = \
@@ -229,13 +229,13 @@ nodist_zebra_zebra_SOURCES = \
# end
zebra_zebra_cumulus_mlag_la_SOURCES = zebra/zebra_mlag_private.c
-zebra_zebra_cumulus_mlag_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+zebra_zebra_cumulus_mlag_la_LDFLAGS = $(MODULE_LDFLAGS)
if LINUX
module_LTLIBRARIES += zebra/dplane_fpm_nl.la
zebra_dplane_fpm_nl_la_SOURCES = zebra/dplane_fpm_nl.c
-zebra_dplane_fpm_nl_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+zebra_dplane_fpm_nl_la_LDFLAGS = $(MODULE_LDFLAGS)
zebra_dplane_fpm_nl_la_LIBADD =
vtysh_scan += zebra/dplane_fpm_nl.c
diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c
index d760432eb1..27fb5d7c22 100644
--- a/zebra/zapi_msg.c
+++ b/zebra/zapi_msg.c
@@ -682,6 +682,8 @@ static int zsend_ipv4_nexthop_lookup_mrib(struct zserv *client,
stream_put_in_addr(s, &addr);
if (re) {
+ struct nexthop_group *nhg;
+
stream_putc(s, re->distance);
stream_putl(s, re->metric);
num = 0;
@@ -689,15 +691,11 @@ static int zsend_ipv4_nexthop_lookup_mrib(struct zserv *client,
nump = stream_get_endp(s);
/* reserve room for nexthop_num */
stream_putc(s, 0);
- /*
- * Only non-recursive routes are elegible to resolve the
- * nexthop we are looking up. Therefore, we will just iterate
- * over the top chain of nexthops.
- */
- for (nexthop = re->nhe->nhg.nexthop; nexthop;
- nexthop = nexthop->next)
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+ nhg = rib_get_fib_nhg(re);
+ for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
+ if (rnh_nexthop_valid(re, nexthop))
num += zserv_encode_nexthop(s, nexthop);
+ }
/* store nexthop_num */
stream_putc_at(s, nump, num);
@@ -3109,6 +3107,8 @@ static void zread_vrf_label(ZAPI_HANDLER_ARGS)
}
zvrf->label[afi] = nlabel;
+ zvrf->label_proto[afi] = client->proto;
+
stream_failure:
return;
}
@@ -3659,8 +3659,8 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = {
[ZEBRA_ADVERTISE_ALL_VNI] = zebra_vxlan_advertise_all_vni,
[ZEBRA_REMOTE_ES_VTEP_ADD] = zebra_evpn_proc_remote_es,
[ZEBRA_REMOTE_ES_VTEP_DEL] = zebra_evpn_proc_remote_es,
- [ZEBRA_REMOTE_VTEP_ADD] = zebra_vxlan_remote_vtep_add,
- [ZEBRA_REMOTE_VTEP_DEL] = zebra_vxlan_remote_vtep_del,
+ [ZEBRA_REMOTE_VTEP_ADD] = zebra_vxlan_remote_vtep_add_zapi,
+ [ZEBRA_REMOTE_VTEP_DEL] = zebra_vxlan_remote_vtep_del_zapi,
[ZEBRA_REMOTE_MACIP_ADD] = zebra_vxlan_remote_macip_add,
[ZEBRA_REMOTE_MACIP_DEL] = zebra_vxlan_remote_macip_del,
[ZEBRA_DUPLICATE_ADDR_DETECTION] = zebra_vxlan_dup_addr_detection,
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index 8e88a73512..2a30fc6eef 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -3656,14 +3656,10 @@ enum zebra_dplane_result dplane_neigh_ip_update(enum dplane_op_e op,
uint16_t state = 0;
uint32_t update_flags;
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
- char buf1[PREFIX_STRLEN], buf2[PREFIX_STRLEN];
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
+ zlog_debug("%s: init link ctx %s: ifp %s, link_ip %pIA ip %pIA",
+ __func__, dplane_op2str(op), ifp->name, link_ip, ip);
- ipaddr2str(link_ip, buf1, sizeof(buf1));
- ipaddr2str(ip, buf2, sizeof(buf2));
- zlog_debug("init link ctx %s: ifp %s, ip %s link %s",
- dplane_op2str(op), ifp->name, buf1, buf2);
- }
if (ndm_state == ZEBRA_NEIGH_STATE_REACHABLE)
state = DPLANE_NUD_REACHABLE;
else if (ndm_state == ZEBRA_NEIGH_STATE_FAILED)
diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c
index 816f46bac9..2c9f1dca59 100644
--- a/zebra/zebra_evpn.c
+++ b/zebra/zebra_evpn.c
@@ -1330,10 +1330,12 @@ void zebra_evpn_cleanup_all(struct hash_bucket *bucket, void *arg)
zebra_evpn_del(zevpn);
}
-static void
-zebra_evpn_process_sync_macip_add(zebra_evpn_t *zevpn, struct ethaddr *macaddr,
- uint16_t ipa_len, struct ipaddr *ipaddr,
- uint8_t flags, uint32_t seq, esi_t *esi)
+static void zebra_evpn_process_sync_macip_add(zebra_evpn_t *zevpn,
+ const struct ethaddr *macaddr,
+ uint16_t ipa_len,
+ const struct ipaddr *ipaddr,
+ uint8_t flags, uint32_t seq,
+ const esi_t *esi)
{
struct sync_mac_ip_ctx ctx;
char ipbuf[INET6_ADDRSTRLEN];
@@ -1380,10 +1382,10 @@ zebra_evpn_process_sync_macip_add(zebra_evpn_t *zevpn, struct ethaddr *macaddr,
/************************** remote mac-ip handling **************************/
/* Process a remote MACIP add from BGP. */
-void process_remote_macip_add(vni_t vni, struct ethaddr *macaddr,
- uint16_t ipa_len, struct ipaddr *ipaddr,
+void zebra_evpn_rem_macip_add(vni_t vni, const struct ethaddr *macaddr,
+ uint16_t ipa_len, const struct ipaddr *ipaddr,
uint8_t flags, uint32_t seq,
- struct in_addr vtep_ip, esi_t *esi)
+ struct in_addr vtep_ip, const esi_t *esi)
{
zebra_evpn_t *zevpn;
zebra_vtep_t *zvtep;
@@ -1447,18 +1449,19 @@ void process_remote_macip_add(vni_t vni, struct ethaddr *macaddr,
return;
- if (process_mac_remote_macip_add(zevpn, zvrf, macaddr, ipa_len, ipaddr,
- &mac, vtep_ip, flags, seq, esi)
+ if (zebra_evpn_mac_remote_macip_add(zevpn, zvrf, macaddr, ipa_len,
+ ipaddr, &mac, vtep_ip, flags, seq,
+ esi)
!= 0)
return;
- process_neigh_remote_macip_add(zevpn, zvrf, ipaddr, mac, vtep_ip, flags,
- seq);
+ zebra_evpn_neigh_remote_macip_add(zevpn, zvrf, ipaddr, mac, vtep_ip,
+ flags, seq);
}
/* Process a remote MACIP delete from BGP. */
-void process_remote_macip_del(vni_t vni, struct ethaddr *macaddr,
- uint16_t ipa_len, struct ipaddr *ipaddr,
+void zebra_evpn_rem_macip_del(vni_t vni, const struct ethaddr *macaddr,
+ uint16_t ipa_len, const struct ipaddr *ipaddr,
struct in_addr vtep_ip)
{
zebra_evpn_t *zevpn;
diff --git a/zebra/zebra_evpn.h b/zebra/zebra_evpn.h
index ee9e1406e4..774627a15d 100644
--- a/zebra/zebra_evpn.h
+++ b/zebra/zebra_evpn.h
@@ -204,12 +204,12 @@ int zebra_evpn_vtep_uninstall(zebra_evpn_t *zevpn, struct in_addr *vtep_ip);
void zebra_evpn_handle_flooding_remote_vteps(struct hash_bucket *bucket,
void *zvrf);
void zebra_evpn_cleanup_all(struct hash_bucket *bucket, void *arg);
-void process_remote_macip_add(vni_t vni, struct ethaddr *macaddr,
- uint16_t ipa_len, struct ipaddr *ipaddr,
+void zebra_evpn_rem_macip_add(vni_t vni, const struct ethaddr *macaddr,
+ uint16_t ipa_len, const struct ipaddr *ipaddr,
uint8_t flags, uint32_t seq,
- struct in_addr vtep_ip, esi_t *esi);
-void process_remote_macip_del(vni_t vni, struct ethaddr *macaddr,
- uint16_t ipa_len, struct ipaddr *ipaddr,
+ struct in_addr vtep_ip, const esi_t *esi);
+void zebra_evpn_rem_macip_del(vni_t vni, const struct ethaddr *macaddr,
+ uint16_t ipa_len, const struct ipaddr *ipaddr,
struct in_addr vtep_ip);
void zebra_evpn_cfg_cleanup(struct hash_bucket *bucket, void *ctxt);
diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c
index fe3167dc29..cf2aa67269 100644
--- a/zebra/zebra_evpn_mac.c
+++ b/zebra/zebra_evpn_mac.c
@@ -986,8 +986,9 @@ void zebra_evpn_print_mac_hash_detail(struct hash_bucket *bucket, void *ctxt)
/*
* Inform BGP about local MACIP.
*/
-int zebra_evpn_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr,
- struct ipaddr *ip, uint8_t flags,
+int zebra_evpn_macip_send_msg_to_client(vni_t vni,
+ const struct ethaddr *macaddr,
+ const struct ipaddr *ip, uint8_t flags,
uint32_t seq, int state,
struct zebra_evpn_es *es, uint16_t cmd)
{
@@ -1095,7 +1096,8 @@ static void *zebra_evpn_mac_alloc(void *p)
/*
* Add MAC entry.
*/
-zebra_mac_t *zebra_evpn_mac_add(zebra_evpn_t *zevpn, struct ethaddr *macaddr)
+zebra_mac_t *zebra_evpn_mac_add(zebra_evpn_t *zevpn,
+ const struct ethaddr *macaddr)
{
zebra_mac_t tmp_mac;
zebra_mac_t *mac = NULL;
@@ -1254,7 +1256,8 @@ void zebra_evpn_mac_del_all(zebra_evpn_t *zevpn, int uninstall, int upd_client,
/*
* Look up MAC hash entry.
*/
-zebra_mac_t *zebra_evpn_mac_lookup(zebra_evpn_t *zevpn, struct ethaddr *mac)
+zebra_mac_t *zebra_evpn_mac_lookup(zebra_evpn_t *zevpn,
+ const struct ethaddr *mac)
{
zebra_mac_t tmp;
zebra_mac_t *pmac;
@@ -1269,7 +1272,7 @@ zebra_mac_t *zebra_evpn_mac_lookup(zebra_evpn_t *zevpn, struct ethaddr *mac)
/*
* Inform BGP about local MAC addition.
*/
-int zebra_evpn_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr,
+int zebra_evpn_mac_send_add_to_client(vni_t vni, const struct ethaddr *macaddr,
uint32_t mac_flags, uint32_t seq,
struct zebra_evpn_es *es)
{
@@ -1303,7 +1306,7 @@ int zebra_evpn_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr,
/*
* Inform BGP about local MAC deletion.
*/
-int zebra_evpn_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr,
+int zebra_evpn_mac_send_del_to_client(vni_t vni, const struct ethaddr *macaddr,
uint32_t flags, bool force)
{
if (!force) {
@@ -1563,7 +1566,7 @@ void zebra_evpn_sync_mac_del(zebra_mac_t *mac)
static inline bool zebra_evpn_mac_is_bgp_seq_ok(zebra_evpn_t *zevpn,
zebra_mac_t *mac, uint32_t seq,
uint16_t ipa_len,
- struct ipaddr *ipaddr,
+ const struct ipaddr *ipaddr,
bool sync)
{
char ipbuf[INET6_ADDRSTRLEN];
@@ -1627,11 +1630,10 @@ static inline bool zebra_evpn_mac_is_bgp_seq_ok(zebra_evpn_t *zevpn,
return true;
}
-zebra_mac_t *
-zebra_evpn_proc_sync_mac_update(zebra_evpn_t *zevpn, struct ethaddr *macaddr,
- uint16_t ipa_len, struct ipaddr *ipaddr,
- uint8_t flags, uint32_t seq, esi_t *esi,
- struct sync_mac_ip_ctx *ctx)
+zebra_mac_t *zebra_evpn_proc_sync_mac_update(
+ zebra_evpn_t *zevpn, const struct ethaddr *macaddr, uint16_t ipa_len,
+ const struct ipaddr *ipaddr, uint8_t flags, uint32_t seq,
+ const esi_t *esi, struct sync_mac_ip_ctx *ctx)
{
zebra_mac_t *mac;
bool inform_bgp = false;
@@ -1958,11 +1960,13 @@ void zebra_evpn_print_dad_mac_hash_detail(struct hash_bucket *bucket,
zebra_evpn_print_mac_hash_detail(bucket, ctxt);
}
-int process_mac_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf,
- struct ethaddr *macaddr, uint16_t ipa_len,
- struct ipaddr *ipaddr, zebra_mac_t **macp,
- struct in_addr vtep_ip, uint8_t flags,
- uint32_t seq, esi_t *esi)
+int zebra_evpn_mac_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf,
+ const struct ethaddr *macaddr,
+ uint16_t ipa_len,
+ const struct ipaddr *ipaddr,
+ zebra_mac_t **macp, struct in_addr vtep_ip,
+ uint8_t flags, uint32_t seq,
+ const esi_t *esi)
{
char buf1[INET6_ADDRSTRLEN];
bool sticky;
@@ -2127,7 +2131,7 @@ int process_mac_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf,
int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn,
struct interface *ifp,
- struct ethaddr *macaddr, vlanid_t vid,
+ const struct ethaddr *macaddr, vlanid_t vid,
bool sticky, bool local_inactive,
bool dp_static, zebra_mac_t *mac)
{
@@ -2447,8 +2451,8 @@ int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, zebra_mac_t *mac,
}
int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn,
- struct ipaddr *ip, zebra_mac_t **macp,
- struct ethaddr *macaddr, vlanid_t vlan_id,
+ const struct ipaddr *ip, zebra_mac_t **macp,
+ const struct ethaddr *macaddr, vlanid_t vlan_id,
bool def_gw)
{
zebra_mac_t *mac;
diff --git a/zebra/zebra_evpn_mac.h b/zebra/zebra_evpn_mac.h
index fb162f1a93..e90082e50b 100644
--- a/zebra/zebra_evpn_mac.h
+++ b/zebra/zebra_evpn_mac.h
@@ -229,11 +229,14 @@ int zebra_evpn_rem_mac_uninstall(zebra_evpn_t *zevi, zebra_mac_t *mac,
int zebra_evpn_rem_mac_install(zebra_evpn_t *zevi, zebra_mac_t *mac,
bool was_static);
void zebra_evpn_deref_ip2mac(zebra_evpn_t *zevi, zebra_mac_t *mac);
-zebra_mac_t *zebra_evpn_mac_lookup(zebra_evpn_t *zevi, struct ethaddr *mac);
-zebra_mac_t *zebra_evpn_mac_add(zebra_evpn_t *zevi, struct ethaddr *macaddr);
+zebra_mac_t *zebra_evpn_mac_lookup(zebra_evpn_t *zevi,
+ const struct ethaddr *mac);
+zebra_mac_t *zebra_evpn_mac_add(zebra_evpn_t *zevi,
+ const struct ethaddr *macaddr);
int zebra_evpn_mac_del(zebra_evpn_t *zevi, zebra_mac_t *mac);
-int zebra_evpn_macip_send_msg_to_client(uint32_t id, struct ethaddr *macaddr,
- struct ipaddr *ip, uint8_t flags,
+int zebra_evpn_macip_send_msg_to_client(uint32_t id,
+ const struct ethaddr *macaddr,
+ const struct ipaddr *ip, uint8_t flags,
uint32_t seq, int state,
struct zebra_evpn_es *es, uint16_t cmd);
void zebra_evpn_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json);
@@ -246,38 +249,39 @@ void zebra_evpn_mac_send_add_del_to_client(zebra_mac_t *mac, bool old_bgp_ready,
void zebra_evpn_mac_del_all(zebra_evpn_t *zevi, int uninstall, int upd_client,
uint32_t flags);
-int zebra_evpn_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr,
+int zebra_evpn_mac_send_add_to_client(vni_t vni, const struct ethaddr *macaddr,
uint32_t mac_flags, uint32_t seq,
struct zebra_evpn_es *es);
-int zebra_evpn_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr,
+int zebra_evpn_mac_send_del_to_client(vni_t vni, const struct ethaddr *macaddr,
uint32_t flags, bool force);
void zebra_evpn_send_mac_list_to_client(zebra_evpn_t *zevi);
-zebra_mac_t *
-zebra_evpn_proc_sync_mac_update(zebra_evpn_t *zevi, struct ethaddr *macaddr,
- uint16_t ipa_len, struct ipaddr *ipaddr,
- uint8_t flags, uint32_t seq, esi_t *esi,
- struct sync_mac_ip_ctx *ctx);
+zebra_mac_t *zebra_evpn_proc_sync_mac_update(
+ zebra_evpn_t *zevi, const struct ethaddr *macaddr, uint16_t ipa_len,
+ const struct ipaddr *ipaddr, uint8_t flags, uint32_t seq,
+ const esi_t *esi, struct sync_mac_ip_ctx *ctx);
void zebra_evpn_sync_mac_del(zebra_mac_t *mac);
void zebra_evpn_rem_mac_del(zebra_evpn_t *zevi, zebra_mac_t *mac);
void zebra_evpn_print_dad_mac_hash(struct hash_bucket *bucket, void *ctxt);
void zebra_evpn_print_dad_mac_hash_detail(struct hash_bucket *bucket,
void *ctxt);
-int process_mac_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf,
- struct ethaddr *macaddr, uint16_t ipa_len,
- struct ipaddr *ipaddr, zebra_mac_t **macp,
- struct in_addr vtep_ip, uint8_t flags,
- uint32_t seq, esi_t *esi);
+int zebra_evpn_mac_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf,
+ const struct ethaddr *macaddr,
+ uint16_t ipa_len,
+ const struct ipaddr *ipaddr,
+ zebra_mac_t **macp, struct in_addr vtep_ip,
+ uint8_t flags, uint32_t seq,
+ const esi_t *esi);
int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn,
struct interface *ifp,
- struct ethaddr *macaddr, vlanid_t vid,
+ const struct ethaddr *macaddr, vlanid_t vid,
bool sticky, bool local_inactive,
bool dp_static, zebra_mac_t *mac);
int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, zebra_mac_t *mac,
bool clear_static);
int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn,
- struct ipaddr *ip, zebra_mac_t **macp,
- struct ethaddr *macaddr, vlanid_t vlan_id,
+ const struct ipaddr *ip, zebra_mac_t **macp,
+ const struct ethaddr *macaddr, vlanid_t vlan_id,
bool def_gw);
void zebra_evpn_mac_svi_add(struct interface *ifp, zebra_evpn_t *zevpn);
void zebra_evpn_mac_svi_del(struct interface *ifp, zebra_evpn_t *zevpn);
diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c
index d6ae92a03d..05947faf4f 100644
--- a/zebra/zebra_evpn_mh.c
+++ b/zebra/zebra_evpn_mh.c
@@ -1747,7 +1747,7 @@ static int zebra_es_rb_cmp(const struct zebra_evpn_es *es1,
RB_GENERATE(zebra_es_rb_head, zebra_evpn_es, rb_node, zebra_es_rb_cmp);
/* Lookup ES */
-struct zebra_evpn_es *zebra_evpn_es_find(esi_t *esi)
+struct zebra_evpn_es *zebra_evpn_es_find(const esi_t *esi)
{
struct zebra_evpn_es tmp;
@@ -1758,7 +1758,7 @@ struct zebra_evpn_es *zebra_evpn_es_find(esi_t *esi)
/* A new local es is created when a local-es-id and sysmac is configured
* against an interface.
*/
-static struct zebra_evpn_es *zebra_evpn_es_new(esi_t *esi)
+static struct zebra_evpn_es *zebra_evpn_es_new(const esi_t *esi)
{
struct zebra_evpn_es *es;
@@ -2392,7 +2392,7 @@ static int zebra_evpn_type3_esi_update(struct zebra_if *zif, uint32_t lid,
return zebra_evpn_local_es_update(zif, &esi);
}
-static int zebra_evpn_remote_es_del(esi_t *esi, struct in_addr vtep_ip)
+int zebra_evpn_remote_es_del(const esi_t *esi, struct in_addr vtep_ip)
{
char buf[ESI_STR_LEN];
struct zebra_evpn_es *es;
@@ -2432,9 +2432,8 @@ static void zebra_evpn_remote_es_flush(struct zebra_evpn_es **esp)
zebra_evpn_es_remote_info_re_eval(esp);
}
-static int zebra_evpn_remote_es_add(esi_t *esi, struct in_addr vtep_ip,
- bool esr_rxed, uint8_t df_alg,
- uint16_t df_pref)
+int zebra_evpn_remote_es_add(const esi_t *esi, struct in_addr vtep_ip,
+ bool esr_rxed, uint8_t df_alg, uint16_t df_pref)
{
char buf[ESI_STR_LEN];
struct zebra_evpn_es *es;
@@ -2498,10 +2497,10 @@ void zebra_evpn_proc_remote_es(ZAPI_HANDLER_ARGS)
: false;
STREAM_GETC(s, df_alg);
STREAM_GETW(s, df_pref);
- zebra_evpn_remote_es_add(&esi, vtep_ip, esr_rxed, df_alg,
- df_pref);
+ zebra_rib_queue_evpn_rem_es_add(&esi, &vtep_ip, esr_rxed,
+ df_alg, df_pref);
} else {
- zebra_evpn_remote_es_del(&esi, vtep_ip);
+ zebra_rib_queue_evpn_rem_es_del(&esi, &vtep_ip);
}
stream_failure:
@@ -2542,7 +2541,7 @@ bool zebra_evpn_es_mac_ref_entry(zebra_mac_t *mac, struct zebra_evpn_es *es)
return true;
}
-bool zebra_evpn_es_mac_ref(zebra_mac_t *mac, esi_t *esi)
+bool zebra_evpn_es_mac_ref(zebra_mac_t *mac, const esi_t *esi)
{
struct zebra_evpn_es *es;
@@ -3901,12 +3900,12 @@ void zebra_evpn_proc_remote_nh(ZAPI_HANDLER_ARGS)
if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
zlog_debug("evpn remote nh %d %pIA rmac %pEA add",
vrf_id, &nh, &rmac);
- zebra_vxlan_evpn_vrf_route_add(vrf_id, &rmac, &nh,
+ zebra_rib_queue_evpn_route_add(vrf_id, &rmac, &nh,
(struct prefix *)&dummy_prefix);
} else {
if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
zlog_debug("evpn remote nh %d %pIA del", vrf_id, &nh);
- zebra_vxlan_evpn_vrf_route_del(vrf_id, &nh,
+ zebra_rib_queue_evpn_route_del(vrf_id, &nh,
(struct prefix *)&dummy_prefix);
}
}
diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h
index 8861e80cee..a828056f1f 100644
--- a/zebra/zebra_evpn_mh.h
+++ b/zebra/zebra_evpn_mh.h
@@ -330,14 +330,17 @@ extern void zebra_evpn_es_show_detail(struct vty *vty, bool uj);
extern void zebra_evpn_es_show_esi(struct vty *vty, bool uj, esi_t *esi);
extern void zebra_evpn_update_all_es(zebra_evpn_t *zevpn);
extern void zebra_evpn_proc_remote_es(ZAPI_HANDLER_ARGS);
+int zebra_evpn_remote_es_add(const esi_t *esi, struct in_addr vtep_ip,
+ bool esr_rxed, uint8_t df_alg, uint16_t df_pref);
+int zebra_evpn_remote_es_del(const esi_t *esi, struct in_addr vtep_ip);
extern void zebra_evpn_es_evi_show(struct vty *vty, bool uj, int detail);
extern void zebra_evpn_es_evi_show_vni(struct vty *vty, bool uj,
vni_t vni, int detail);
extern void zebra_evpn_es_mac_deref_entry(zebra_mac_t *mac);
extern bool zebra_evpn_es_mac_ref_entry(zebra_mac_t *mac,
- struct zebra_evpn_es *es);
-extern bool zebra_evpn_es_mac_ref(zebra_mac_t *mac, esi_t *esi);
-extern struct zebra_evpn_es *zebra_evpn_es_find(esi_t *esi);
+ struct zebra_evpn_es *es);
+extern bool zebra_evpn_es_mac_ref(zebra_mac_t *mac, const esi_t *esi);
+extern struct zebra_evpn_es *zebra_evpn_es_find(const esi_t *esi);
extern void zebra_evpn_interface_init(void);
extern int zebra_evpn_mh_if_write(struct vty *vty, struct interface *ifp);
extern void zebra_evpn_acc_vl_show(struct vty *vty, bool uj);
diff --git a/zebra/zebra_evpn_neigh.c b/zebra/zebra_evpn_neigh.c
index 4c7a1542fc..839e8d9ebc 100644
--- a/zebra/zebra_evpn_neigh.c
+++ b/zebra/zebra_evpn_neigh.c
@@ -204,7 +204,7 @@ static void *zebra_evpn_neigh_alloc(void *p)
}
static void zebra_evpn_local_neigh_ref_mac(zebra_neigh_t *n,
- struct ethaddr *macaddr,
+ const struct ethaddr *macaddr,
zebra_mac_t *mac,
bool send_mac_update)
{
@@ -284,8 +284,8 @@ static void zebra_evpn_sync_neigh_dp_install(zebra_neigh_t *n,
/*
* Inform BGP about local neighbor addition.
*/
-int zebra_evpn_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip,
- struct ethaddr *macaddr,
+int zebra_evpn_neigh_send_add_to_client(vni_t vni, const struct ipaddr *ip,
+ const struct ethaddr *macaddr,
zebra_mac_t *zmac, uint32_t neigh_flags,
uint32_t seq)
{
@@ -497,7 +497,7 @@ static void zebra_evpn_local_neigh_deref_mac(zebra_neigh_t *n,
}
bool zebra_evpn_neigh_is_bgp_seq_ok(zebra_evpn_t *zevpn, zebra_neigh_t *n,
- struct ethaddr *macaddr, uint32_t seq,
+ const struct ethaddr *macaddr, uint32_t seq,
bool sync)
{
uint32_t tmp_seq;
@@ -543,8 +543,8 @@ bool zebra_evpn_neigh_is_bgp_seq_ok(zebra_evpn_t *zevpn, zebra_neigh_t *n,
* Add neighbor entry.
*/
static zebra_neigh_t *zebra_evpn_neigh_add(zebra_evpn_t *zevpn,
- struct ipaddr *ip,
- struct ethaddr *mac,
+ const struct ipaddr *ip,
+ const struct ethaddr *mac,
zebra_mac_t *zmac, uint32_t n_flags)
{
zebra_neigh_t tmp_n;
@@ -615,8 +615,8 @@ void zebra_evpn_sync_neigh_del(zebra_neigh_t *n)
zebra_neigh_t *
zebra_evpn_proc_sync_neigh_update(zebra_evpn_t *zevpn, zebra_neigh_t *n,
- uint16_t ipa_len, struct ipaddr *ipaddr,
- uint8_t flags, uint32_t seq, esi_t *esi,
+ uint16_t ipa_len, const struct ipaddr *ipaddr,
+ uint8_t flags, uint32_t seq, const esi_t *esi,
struct sync_mac_ip_ctx *ctx)
{
struct interface *ifp = NULL;
@@ -895,7 +895,8 @@ void zebra_evpn_neigh_del_all(zebra_evpn_t *zevpn, int uninstall,
/*
* Look up neighbor hash entry.
*/
-zebra_neigh_t *zebra_evpn_neigh_lookup(zebra_evpn_t *zevpn, struct ipaddr *ip)
+zebra_neigh_t *zebra_evpn_neigh_lookup(zebra_evpn_t *zevpn,
+ const struct ipaddr *ip)
{
zebra_neigh_t tmp;
zebra_neigh_t *n;
@@ -1254,9 +1255,9 @@ zebra_evpn_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf, zebra_neigh_t *nbr,
}
int zebra_evpn_local_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp,
- struct ipaddr *ip, struct ethaddr *macaddr,
- bool is_router, bool local_inactive,
- bool dp_static)
+ const struct ipaddr *ip,
+ const struct ethaddr *macaddr, bool is_router,
+ bool local_inactive, bool dp_static)
{
struct zebra_vrf *zvrf;
zebra_neigh_t *n = NULL;
@@ -1596,7 +1597,8 @@ int zebra_evpn_local_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp,
}
int zebra_evpn_remote_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp,
- struct ipaddr *ip, struct ethaddr *macaddr,
+ const struct ipaddr *ip,
+ const struct ethaddr *macaddr,
uint16_t state)
{
zebra_neigh_t *n = NULL;
@@ -2046,10 +2048,11 @@ void zebra_evpn_print_dad_neigh_hash_detail(struct hash_bucket *bucket,
zebra_evpn_print_neigh_hash_detail(bucket, ctxt);
}
-void process_neigh_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf,
- struct ipaddr *ipaddr, zebra_mac_t *mac,
- struct in_addr vtep_ip, uint8_t flags,
- uint32_t seq)
+void zebra_evpn_neigh_remote_macip_add(zebra_evpn_t *zevpn,
+ struct zebra_vrf *zvrf,
+ const struct ipaddr *ipaddr,
+ zebra_mac_t *mac, struct in_addr vtep_ip,
+ uint8_t flags, uint32_t seq)
{
zebra_neigh_t *n;
int update_neigh = 0;
@@ -2240,7 +2243,8 @@ int zebra_evpn_neigh_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn,
void zebra_evpn_neigh_remote_uninstall(zebra_evpn_t *zevpn,
struct zebra_vrf *zvrf, zebra_neigh_t *n,
- zebra_mac_t *mac, struct ipaddr *ipaddr)
+ zebra_mac_t *mac,
+ const struct ipaddr *ipaddr)
{
if (zvrf->dad_freeze && CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)
&& CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)
@@ -2273,7 +2277,7 @@ void zebra_evpn_neigh_remote_uninstall(zebra_evpn_t *zevpn,
}
}
-int zebra_evpn_neigh_del_ip(zebra_evpn_t *zevpn, struct ipaddr *ip)
+int zebra_evpn_neigh_del_ip(zebra_evpn_t *zevpn, const struct ipaddr *ip)
{
zebra_neigh_t *n;
zebra_mac_t *zmac;
@@ -2298,7 +2302,7 @@ int zebra_evpn_neigh_del_ip(zebra_evpn_t *zevpn, struct ipaddr *ip)
}
/* If it is a remote entry, the kernel has aged this out or someone has
- * deleted it, it needs to be re-installed as Quagga is the owner.
+ * deleted it, it needs to be re-installed as FRR is the owner.
*/
if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
zebra_evpn_rem_neigh_install(zevpn, n, false /*was_static*/);
diff --git a/zebra/zebra_evpn_neigh.h b/zebra/zebra_evpn_neigh.h
index 05156c1255..3735a833fd 100644
--- a/zebra/zebra_evpn_neigh.h
+++ b/zebra/zebra_evpn_neigh.h
@@ -217,26 +217,27 @@ int remote_neigh_count(zebra_mac_t *zmac);
int zebra_evpn_rem_neigh_install(zebra_evpn_t *zevpn, zebra_neigh_t *n,
bool was_static);
void zebra_evpn_install_neigh_hash(struct hash_bucket *bucket, void *ctxt);
-int zebra_evpn_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip,
- struct ethaddr *macaddr,
+int zebra_evpn_neigh_send_add_to_client(vni_t vni, const struct ipaddr *ip,
+ const struct ethaddr *macaddr,
zebra_mac_t *zmac, uint32_t neigh_flags,
uint32_t seq);
int zebra_evpn_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip,
struct ethaddr *macaddr, uint32_t flags,
int state, bool force);
bool zebra_evpn_neigh_is_bgp_seq_ok(zebra_evpn_t *zevpn, zebra_neigh_t *n,
- struct ethaddr *macaddr, uint32_t seq,
+ const struct ethaddr *macaddr, uint32_t seq,
bool sync);
int zebra_evpn_neigh_del(zebra_evpn_t *zevpn, zebra_neigh_t *n);
void zebra_evpn_sync_neigh_del(zebra_neigh_t *n);
zebra_neigh_t *
zebra_evpn_proc_sync_neigh_update(zebra_evpn_t *zevpn, zebra_neigh_t *n,
- uint16_t ipa_len, struct ipaddr *ipaddr,
- uint8_t flags, uint32_t seq, esi_t *esi,
+ uint16_t ipa_len, const struct ipaddr *ipaddr,
+ uint8_t flags, uint32_t seq, const esi_t *esi,
struct sync_mac_ip_ctx *ctx);
void zebra_evpn_neigh_del_all(zebra_evpn_t *zevpn, int uninstall,
int upd_client, uint32_t flags);
-zebra_neigh_t *zebra_evpn_neigh_lookup(zebra_evpn_t *zevpn, struct ipaddr *ip);
+zebra_neigh_t *zebra_evpn_neigh_lookup(zebra_evpn_t *zevpn,
+ const struct ipaddr *ip);
int zebra_evpn_rem_neigh_install(zebra_evpn_t *zevpn, zebra_neigh_t *n,
bool was_static);
@@ -251,11 +252,12 @@ void zebra_evpn_process_neigh_on_local_mac_change(zebra_evpn_t *zevpn,
void zebra_evpn_process_neigh_on_remote_mac_del(zebra_evpn_t *zevpn,
zebra_mac_t *zmac);
int zebra_evpn_local_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp,
- struct ipaddr *ip, struct ethaddr *macaddr,
- bool is_router, bool local_inactive,
- bool dp_static);
+ const struct ipaddr *ip,
+ const struct ethaddr *macaddr, bool is_router,
+ bool local_inactive, bool dp_static);
int zebra_evpn_remote_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp,
- struct ipaddr *ip, struct ethaddr *macaddr,
+ const struct ipaddr *ip,
+ const struct ethaddr *macaddr,
uint16_t state);
void zebra_evpn_send_neigh_to_client(zebra_evpn_t *zevpn);
void zebra_evpn_clear_dup_neigh_hash(struct hash_bucket *bucket, void *ctxt);
@@ -266,16 +268,18 @@ void zebra_evpn_print_neigh_hash_detail(struct hash_bucket *bucket, void *ctxt);
void zebra_evpn_print_dad_neigh_hash(struct hash_bucket *bucket, void *ctxt);
void zebra_evpn_print_dad_neigh_hash_detail(struct hash_bucket *bucket,
void *ctxt);
-void process_neigh_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf,
- struct ipaddr *ipaddr, zebra_mac_t *mac,
- struct in_addr vtep_ip, uint8_t flags,
- uint32_t seq);
+void zebra_evpn_neigh_remote_macip_add(zebra_evpn_t *zevpn,
+ struct zebra_vrf *zvrf,
+ const struct ipaddr *ipaddr,
+ zebra_mac_t *mac, struct in_addr vtep_ip,
+ uint8_t flags, uint32_t seq);
int zebra_evpn_neigh_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn,
struct ipaddr *ip, zebra_mac_t *mac);
void zebra_evpn_neigh_remote_uninstall(zebra_evpn_t *zevpn,
struct zebra_vrf *zvrf, zebra_neigh_t *n,
- zebra_mac_t *mac, struct ipaddr *ipaddr);
-int zebra_evpn_neigh_del_ip(zebra_evpn_t *zevpn, struct ipaddr *ip);
+ zebra_mac_t *mac,
+ const struct ipaddr *ipaddr);
+int zebra_evpn_neigh_del_ip(zebra_evpn_t *zevpn, const struct ipaddr *ip);
#ifdef __cplusplus
diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c
index a2d1513ce4..66d2d6b4ba 100644
--- a/zebra/zebra_mpls.c
+++ b/zebra/zebra_mpls.c
@@ -3932,6 +3932,40 @@ void zebra_mpls_cleanup_tables(struct zebra_vrf *zvrf)
}
/*
+ * When a vrf label is assigned and the client goes away
+ * we should cleanup the vrf labels associated with
+ * that zclient.
+ */
+void zebra_mpls_client_cleanup_vrf_label(uint8_t proto)
+{
+ struct vrf *vrf;
+ struct zebra_vrf *def_zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
+
+ if (def_zvrf == NULL)
+ return;
+
+ RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
+ struct zebra_vrf *zvrf = vrf->info;
+ afi_t afi;
+
+ if (!zvrf)
+ continue;
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ if (zvrf->label_proto[afi] == proto
+ && zvrf->label[afi] != MPLS_LABEL_NONE)
+ lsp_uninstall(def_zvrf, zvrf->label[afi]);
+
+ /*
+ * Cleanup data structures by fiat
+ */
+ zvrf->label_proto[afi] = 0;
+ zvrf->label[afi] = MPLS_LABEL_NONE;
+ }
+ }
+}
+
+/*
* Called upon process exiting, need to delete LSP forwarding
* entries from the kernel.
* NOTE: Currently supported only for default VRF.
diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h
index 7059d393ed..5195b2f14f 100644
--- a/zebra/zebra_mpls.h
+++ b/zebra/zebra_mpls.h
@@ -416,6 +416,12 @@ void zebra_mpls_init(void);
*/
void zebra_mpls_vty_init(void);
+/*
+ * When cleaning up a client connection ensure that there are no
+ * vrf labels that need cleaning up too
+ */
+void zebra_mpls_client_cleanup_vrf_label(uint8_t proto);
+
/* Inline functions. */
/*
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
index af86263a16..46d5164127 100644
--- a/zebra/zebra_nhg.c
+++ b/zebra/zebra_nhg.c
@@ -453,8 +453,13 @@ static void *zebra_nhg_hash_alloc(void *arg)
/* Mark duplicate nexthops in a group at creation time. */
nexthop_group_mark_duplicates(&(nhe->nhg));
- /* Add the ifp now if it's not a group or recursive and has ifindex */
- if (nhe->nhg.nexthop && nhe->nhg.nexthop->ifindex) {
+ /*
+ * Add the ifp now if it's not a group or recursive and has ifindex.
+ *
+ * A proto-owned ID is always a group.
+ */
+ if (!PROTO_OWNED(nhe) && nhe->nhg.nexthop && !nhe->nhg.nexthop->next
+ && !nhe->nhg.nexthop->resolved && nhe->nhg.nexthop->ifindex) {
struct interface *ifp = NULL;
ifp = if_lookup_by_index(nhe->nhg.nexthop->ifindex,
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 12cc0b4e8a..c51dd759a6 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -56,13 +56,14 @@
#include "zebra/zebra_vxlan.h"
#include "zebra/zapi_msg.h"
#include "zebra/zebra_dplane.h"
+#include "zebra/zebra_evpn_mh.h"
DEFINE_MGROUP(ZEBRA, "zebra");
DEFINE_MTYPE(ZEBRA, RE, "Route Entry");
DEFINE_MTYPE_STATIC(ZEBRA, RIB_DEST, "RIB destination");
DEFINE_MTYPE_STATIC(ZEBRA, RIB_UPDATE_CTX, "Rib update context object");
-DEFINE_MTYPE_STATIC(ZEBRA, WQ_NHG_WRAPPER, "WQ nhg wrapper");
+DEFINE_MTYPE_STATIC(ZEBRA, WQ_WRAPPER, "WQ wrapper");
/*
* Event, list, and mutex for delivery of dataplane results
@@ -74,7 +75,7 @@ static struct dplane_ctx_q rib_dplane_q;
DEFINE_HOOK(rib_update, (struct route_node * rn, const char *reason),
(rn, reason));
-/* Should we allow non Quagga processes to delete our routes */
+/* Should we allow non FRR processes to delete our routes */
extern int allow_delete;
/* Each route type's string and default distance value. */
@@ -83,41 +84,44 @@ static const struct {
uint8_t distance;
uint8_t meta_q_map;
} route_info[ZEBRA_ROUTE_MAX] = {
- [ZEBRA_ROUTE_NHG] = {ZEBRA_ROUTE_NHG, 255 /* Uneeded for nhg's */, 0},
- [ZEBRA_ROUTE_SYSTEM] = {ZEBRA_ROUTE_SYSTEM, 0, 6},
- [ZEBRA_ROUTE_KERNEL] = {ZEBRA_ROUTE_KERNEL, 0, 2},
- [ZEBRA_ROUTE_CONNECT] = {ZEBRA_ROUTE_CONNECT, 0, 1},
- [ZEBRA_ROUTE_STATIC] = {ZEBRA_ROUTE_STATIC, 1, 3},
- [ZEBRA_ROUTE_RIP] = {ZEBRA_ROUTE_RIP, 120, 4},
- [ZEBRA_ROUTE_RIPNG] = {ZEBRA_ROUTE_RIPNG, 120, 4},
- [ZEBRA_ROUTE_OSPF] = {ZEBRA_ROUTE_OSPF, 110, 4},
- [ZEBRA_ROUTE_OSPF6] = {ZEBRA_ROUTE_OSPF6, 110, 4},
- [ZEBRA_ROUTE_ISIS] = {ZEBRA_ROUTE_ISIS, 115, 4},
- [ZEBRA_ROUTE_BGP] = {ZEBRA_ROUTE_BGP, 20 /* IBGP is 200. */, 5},
- [ZEBRA_ROUTE_PIM] = {ZEBRA_ROUTE_PIM, 255, 6},
- [ZEBRA_ROUTE_EIGRP] = {ZEBRA_ROUTE_EIGRP, 90, 4},
- [ZEBRA_ROUTE_NHRP] = {ZEBRA_ROUTE_NHRP, 10, 4},
- [ZEBRA_ROUTE_HSLS] = {ZEBRA_ROUTE_HSLS, 255, 6},
- [ZEBRA_ROUTE_OLSR] = {ZEBRA_ROUTE_OLSR, 255, 6},
- [ZEBRA_ROUTE_TABLE] = {ZEBRA_ROUTE_TABLE, 150, 3},
- [ZEBRA_ROUTE_LDP] = {ZEBRA_ROUTE_LDP, 150, 6},
- [ZEBRA_ROUTE_VNC] = {ZEBRA_ROUTE_VNC, 20, 5},
- [ZEBRA_ROUTE_VNC_DIRECT] = {ZEBRA_ROUTE_VNC_DIRECT, 20, 5},
- [ZEBRA_ROUTE_VNC_DIRECT_RH] = {ZEBRA_ROUTE_VNC_DIRECT_RH, 20, 5},
- [ZEBRA_ROUTE_BGP_DIRECT] = {ZEBRA_ROUTE_BGP_DIRECT, 20, 5},
- [ZEBRA_ROUTE_BGP_DIRECT_EXT] = {ZEBRA_ROUTE_BGP_DIRECT_EXT, 20, 5},
- [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 100, 4},
- [ZEBRA_ROUTE_SHARP] = {ZEBRA_ROUTE_SHARP, 150, 6},
- [ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 6},
- [ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, 6},
- [ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, 115, 4},
- [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 6},
- [ZEBRA_ROUTE_SRTE] = {ZEBRA_ROUTE_SRTE, 255, 6},
+ [ZEBRA_ROUTE_NHG] = {ZEBRA_ROUTE_NHG, 255 /* Unneeded for nhg's */, 0},
+ [ZEBRA_ROUTE_SYSTEM] = {ZEBRA_ROUTE_SYSTEM, 0, 7},
+ [ZEBRA_ROUTE_KERNEL] = {ZEBRA_ROUTE_KERNEL, 0, 3},
+ [ZEBRA_ROUTE_CONNECT] = {ZEBRA_ROUTE_CONNECT, 0, 2},
+ [ZEBRA_ROUTE_STATIC] = {ZEBRA_ROUTE_STATIC, 1, 4},
+ [ZEBRA_ROUTE_RIP] = {ZEBRA_ROUTE_RIP, 120, 5},
+ [ZEBRA_ROUTE_RIPNG] = {ZEBRA_ROUTE_RIPNG, 120, 5},
+ [ZEBRA_ROUTE_OSPF] = {ZEBRA_ROUTE_OSPF, 110, 5},
+ [ZEBRA_ROUTE_OSPF6] = {ZEBRA_ROUTE_OSPF6, 110, 5},
+ [ZEBRA_ROUTE_ISIS] = {ZEBRA_ROUTE_ISIS, 115, 5},
+ [ZEBRA_ROUTE_BGP] = {ZEBRA_ROUTE_BGP, 20 /* IBGP is 200. */, 6},
+ [ZEBRA_ROUTE_PIM] = {ZEBRA_ROUTE_PIM, 255, 7},
+ [ZEBRA_ROUTE_EIGRP] = {ZEBRA_ROUTE_EIGRP, 90, 5},
+ [ZEBRA_ROUTE_NHRP] = {ZEBRA_ROUTE_NHRP, 10, 5},
+ [ZEBRA_ROUTE_HSLS] = {ZEBRA_ROUTE_HSLS, 255, 7},
+ [ZEBRA_ROUTE_OLSR] = {ZEBRA_ROUTE_OLSR, 255, 7},
+ [ZEBRA_ROUTE_TABLE] = {ZEBRA_ROUTE_TABLE, 150, 4},
+ [ZEBRA_ROUTE_LDP] = {ZEBRA_ROUTE_LDP, 150, 7},
+ [ZEBRA_ROUTE_VNC] = {ZEBRA_ROUTE_VNC, 20, 6},
+ [ZEBRA_ROUTE_VNC_DIRECT] = {ZEBRA_ROUTE_VNC_DIRECT, 20, 6},
+ [ZEBRA_ROUTE_VNC_DIRECT_RH] = {ZEBRA_ROUTE_VNC_DIRECT_RH, 20, 6},
+ [ZEBRA_ROUTE_BGP_DIRECT] = {ZEBRA_ROUTE_BGP_DIRECT, 20, 6},
+ [ZEBRA_ROUTE_BGP_DIRECT_EXT] = {ZEBRA_ROUTE_BGP_DIRECT_EXT, 20, 6},
+ [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 100, 5},
+ [ZEBRA_ROUTE_SHARP] = {ZEBRA_ROUTE_SHARP, 150, 7},
+ [ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 7},
+ [ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, 7},
+ [ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, 115, 5},
+ [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 7},
+ [ZEBRA_ROUTE_SRTE] = {ZEBRA_ROUTE_SRTE, 255, 7},
/* Any new route type added to zebra, should be mirrored here */
/* no entry/default: 150 */
};
+/* EVPN/VXLAN subqueue is number 1 */
+#define META_QUEUE_EVPN 1
+
/* Wrapper struct for nhg workqueue items; a 'ctx' is an incoming update
* from the OS, and an 'nhe' is a nhe update.
*/
@@ -132,6 +136,29 @@ struct wq_nhg_wrapper {
#define WQ_NHG_WRAPPER_TYPE_CTX 0x01
#define WQ_NHG_WRAPPER_TYPE_NHG 0x02
+/* Wrapper structs for evpn/vxlan workqueue items. */
+struct wq_evpn_wrapper {
+ int type;
+ bool add_p;
+ vrf_id_t vrf_id;
+ bool esr_rxed;
+ uint8_t df_alg;
+ uint16_t df_pref;
+ uint32_t flags;
+ uint32_t seq;
+ esi_t esi;
+ vni_t vni;
+ struct ipaddr ip;
+ struct ethaddr macaddr;
+ struct prefix prefix;
+ struct in_addr vtep_ip;
+};
+
+#define WQ_EVPN_WRAPPER_TYPE_VRFROUTE 0x01
+#define WQ_EVPN_WRAPPER_TYPE_REM_ES 0x02
+#define WQ_EVPN_WRAPPER_TYPE_REM_MACIP 0x03
+#define WQ_EVPN_WRAPPER_TYPE_REM_VTEP 0x04
+
/* %pRN is already a printer for route_nodes that just prints the prefix */
#ifdef _FRR_ATTRIBUTE_PRINTFRR
#pragma FRR printfrr_ext "%pZN" (struct route_node *)
@@ -2316,6 +2343,62 @@ done:
}
/*
+ * Process a node from the EVPN/VXLAN subqueue.
+ */
+static void process_subq_evpn(struct listnode *lnode)
+{
+ struct wq_evpn_wrapper *w;
+
+ /* In general, the list node points to a wrapper object
+ * holding the info necessary to make some update.
+ */
+ w = listgetdata(lnode);
+ if (!w)
+ return;
+
+ if (w->type == WQ_EVPN_WRAPPER_TYPE_VRFROUTE) {
+ if (w->add_p)
+ zebra_vxlan_evpn_vrf_route_add(w->vrf_id, &w->macaddr,
+ &w->ip, &w->prefix);
+ else
+ zebra_vxlan_evpn_vrf_route_del(w->vrf_id, &w->ip,
+ &w->prefix);
+ } else if (w->type == WQ_EVPN_WRAPPER_TYPE_REM_ES) {
+ if (w->add_p)
+ zebra_evpn_remote_es_add(&w->esi, w->ip.ipaddr_v4,
+ w->esr_rxed, w->df_alg,
+ w->df_pref);
+ else
+ zebra_evpn_remote_es_del(&w->esi, w->ip.ipaddr_v4);
+ } else if (w->type == WQ_EVPN_WRAPPER_TYPE_REM_MACIP) {
+ uint16_t ipa_len = 0;
+
+ if (w->ip.ipa_type == IPADDR_V4)
+ ipa_len = IPV4_MAX_BYTELEN;
+ else if (w->ip.ipa_type == IPADDR_V6)
+ ipa_len = IPV6_MAX_BYTELEN;
+
+ if (w->add_p)
+ zebra_evpn_rem_macip_add(w->vni, &w->macaddr, ipa_len,
+ &w->ip, w->flags, w->seq,
+ w->vtep_ip, &w->esi);
+ else
+ zebra_evpn_rem_macip_del(w->vni, &w->macaddr, ipa_len,
+ &w->ip, w->vtep_ip);
+ } else if (w->type == WQ_EVPN_WRAPPER_TYPE_REM_VTEP) {
+ if (w->add_p)
+ zebra_vxlan_remote_vtep_add(w->vrf_id, w->vni,
+ w->vtep_ip, w->flags);
+ else
+ zebra_vxlan_remote_vtep_del(w->vrf_id, w->vni,
+ w->vtep_ip);
+ }
+
+
+ XFREE(MTYPE_WQ_WRAPPER, w);
+}
+
+/*
* Process the nexthop-group workqueue subqueue
*/
static void process_subq_nhg(struct listnode *lnode)
@@ -2355,8 +2438,7 @@ static void process_subq_nhg(struct listnode *lnode)
/* Process incoming nhg update, probably from a proto daemon */
newnhe = zebra_nhg_proto_add(nhe->id, nhe->type,
nhe->zapi_instance,
- nhe->zapi_session,
- &nhe->nhg, 0);
+ nhe->zapi_session, &nhe->nhg, 0);
/* Report error to daemon via ZAPI */
if (newnhe == NULL)
@@ -2368,7 +2450,7 @@ static void process_subq_nhg(struct listnode *lnode)
zebra_nhg_free(nhe);
}
- XFREE(MTYPE_WQ_NHG_WRAPPER, w);
+ XFREE(MTYPE_WQ_WRAPPER, w);
}
static void process_subq_route(struct listnode *lnode, uint8_t qindex)
@@ -2411,9 +2493,9 @@ static void process_subq_route(struct listnode *lnode, uint8_t qindex)
route_unlock_node(rnode);
}
-/* Take a list of route_node structs and return 1, if there was a record
- * picked from it and processed by rib_process(). Don't process more,
- * than one RN record; operate only in the specified sub-queue.
+/*
+ * Examine the specified subqueue; process one entry and return 1 if
+ * there is a node, return 0 otherwise.
*/
static unsigned int process_subq(struct list *subq, uint8_t qindex)
{
@@ -2422,7 +2504,9 @@ static unsigned int process_subq(struct list *subq, uint8_t qindex)
if (!lnode)
return 0;
- if (qindex == route_info[ZEBRA_ROUTE_NHG].meta_q_map)
+ if (qindex == META_QUEUE_EVPN)
+ process_subq_evpn(lnode);
+ else if (qindex == route_info[ZEBRA_ROUTE_NHG].meta_q_map)
process_subq_nhg(lnode);
else
process_subq_route(lnode, qindex);
@@ -2432,7 +2516,7 @@ static unsigned int process_subq(struct list *subq, uint8_t qindex)
return 1;
}
-/* Dispatch the meta queue by picking, processing and unlocking the next RN from
+/* Dispatch the meta queue by picking and processing the next node from
* a non-empty sub-queue with lowest priority. wq is equal to zebra->ribq and
* data is pointed to the meta queue structure.
*/
@@ -2538,7 +2622,7 @@ static int rib_meta_queue_nhg_ctx_add(struct meta_queue *mq, void *data)
if (!ctx)
return -1;
- w = XCALLOC(MTYPE_WQ_NHG_WRAPPER, sizeof(struct wq_nhg_wrapper));
+ w = XCALLOC(MTYPE_WQ_WRAPPER, sizeof(struct wq_nhg_wrapper));
w->type = WQ_NHG_WRAPPER_TYPE_CTX;
w->u.ctx = ctx;
@@ -2564,7 +2648,7 @@ static int rib_meta_queue_nhg_add(struct meta_queue *mq, void *data)
if (!nhe)
return -1;
- w = XCALLOC(MTYPE_WQ_NHG_WRAPPER, sizeof(struct wq_nhg_wrapper));
+ w = XCALLOC(MTYPE_WQ_WRAPPER, sizeof(struct wq_nhg_wrapper));
w->type = WQ_NHG_WRAPPER_TYPE_NHG;
w->u.nhe = nhe;
@@ -2579,6 +2663,14 @@ static int rib_meta_queue_nhg_add(struct meta_queue *mq, void *data)
return 0;
}
+static int rib_meta_queue_evpn_add(struct meta_queue *mq, void *data)
+{
+ listnode_add(mq->subq[META_QUEUE_EVPN], data);
+ mq->size++;
+
+ return 0;
+}
+
static int mq_add_handler(void *data,
int (*mq_add_func)(struct meta_queue *mq, void *data))
{
@@ -2640,6 +2732,225 @@ int rib_queue_nhe_add(struct nhg_hash_entry *nhe)
return mq_add_handler(nhe, rib_meta_queue_nhg_add);
}
+/*
+ * Enqueue evpn route for processing
+ */
+int zebra_rib_queue_evpn_route_add(vrf_id_t vrf_id, const struct ethaddr *rmac,
+ const struct ipaddr *vtep_ip,
+ const struct prefix *host_prefix)
+{
+ struct wq_evpn_wrapper *w;
+
+ w = XCALLOC(MTYPE_WQ_WRAPPER, sizeof(struct wq_evpn_wrapper));
+
+ w->type = WQ_EVPN_WRAPPER_TYPE_VRFROUTE;
+ w->add_p = true;
+ w->vrf_id = vrf_id;
+ w->macaddr = *rmac;
+ w->ip = *vtep_ip;
+ w->prefix = *host_prefix;
+
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("%s: (%u)%pIA, host prefix %pFX enqueued", __func__,
+ vrf_id, vtep_ip, host_prefix);
+
+ return mq_add_handler(w, rib_meta_queue_evpn_add);
+}
+
+int zebra_rib_queue_evpn_route_del(vrf_id_t vrf_id,
+ const struct ipaddr *vtep_ip,
+ const struct prefix *host_prefix)
+{
+ struct wq_evpn_wrapper *w;
+
+ w = XCALLOC(MTYPE_WQ_WRAPPER, sizeof(struct wq_evpn_wrapper));
+
+ w->type = WQ_EVPN_WRAPPER_TYPE_VRFROUTE;
+ w->add_p = false;
+ w->vrf_id = vrf_id;
+ w->ip = *vtep_ip;
+ w->prefix = *host_prefix;
+
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("%s: (%u)%pIA, host prefix %pFX enqueued", __func__,
+ vrf_id, vtep_ip, host_prefix);
+
+ return mq_add_handler(w, rib_meta_queue_evpn_add);
+}
+
+/* Enqueue EVPN remote ES for processing */
+int zebra_rib_queue_evpn_rem_es_add(const esi_t *esi,
+ const struct in_addr *vtep_ip,
+ bool esr_rxed, uint8_t df_alg,
+ uint16_t df_pref)
+{
+ struct wq_evpn_wrapper *w;
+ char buf[ESI_STR_LEN];
+
+ w = XCALLOC(MTYPE_WQ_WRAPPER, sizeof(struct wq_evpn_wrapper));
+
+ w->type = WQ_EVPN_WRAPPER_TYPE_REM_ES;
+ w->add_p = true;
+ w->esi = *esi;
+ w->ip.ipa_type = IPADDR_V4;
+ w->ip.ipaddr_v4 = *vtep_ip;
+ w->esr_rxed = esr_rxed;
+ w->df_alg = df_alg;
+ w->df_pref = df_pref;
+
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("%s: vtep %pI4, esi %s enqueued", __func__, vtep_ip,
+ esi_to_str(esi, buf, sizeof(buf)));
+
+ return mq_add_handler(w, rib_meta_queue_evpn_add);
+}
+
+int zebra_rib_queue_evpn_rem_es_del(const esi_t *esi,
+ const struct in_addr *vtep_ip)
+{
+ struct wq_evpn_wrapper *w;
+ char buf[ESI_STR_LEN];
+
+ w = XCALLOC(MTYPE_WQ_WRAPPER, sizeof(struct wq_evpn_wrapper));
+
+ w->type = WQ_EVPN_WRAPPER_TYPE_REM_ES;
+ w->add_p = false;
+ w->esi = *esi;
+ w->ip.ipa_type = IPADDR_V4;
+ w->ip.ipaddr_v4 = *vtep_ip;
+
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
+ if (memcmp(esi, zero_esi, sizeof(esi_t)) != 0)
+ esi_to_str(esi, buf, sizeof(buf));
+ else
+ strlcpy(buf, "-", sizeof(buf));
+
+ zlog_debug("%s: vtep %pI4, esi %s enqueued", __func__, vtep_ip,
+ buf);
+ }
+
+ return mq_add_handler(w, rib_meta_queue_evpn_add);
+}
+
+/*
+ * Enqueue EVPN remote macip update for processing
+ */
+int zebra_rib_queue_evpn_rem_macip_add(vni_t vni, const struct ethaddr *macaddr,
+ const struct ipaddr *ipaddr,
+ uint8_t flags, uint32_t seq,
+ struct in_addr vtep_ip, const esi_t *esi)
+{
+ struct wq_evpn_wrapper *w;
+ char buf[ESI_STR_LEN];
+
+ w = XCALLOC(MTYPE_WQ_WRAPPER, sizeof(struct wq_evpn_wrapper));
+
+ w->type = WQ_EVPN_WRAPPER_TYPE_REM_MACIP;
+ w->add_p = true;
+ w->vni = vni;
+ w->macaddr = *macaddr;
+ w->ip = *ipaddr;
+ w->flags = flags;
+ w->seq = seq;
+ w->vtep_ip = vtep_ip;
+ w->esi = *esi;
+
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
+ if (memcmp(esi, zero_esi, sizeof(esi_t)) != 0)
+ esi_to_str(esi, buf, sizeof(buf));
+ else
+ strlcpy(buf, "-", sizeof(buf));
+
+ zlog_debug("%s: mac %pEA, vtep %pI4, esi %s enqueued", __func__,
+ macaddr, &vtep_ip, buf);
+ }
+
+ return mq_add_handler(w, rib_meta_queue_evpn_add);
+}
+
+int zebra_rib_queue_evpn_rem_macip_del(vni_t vni, const struct ethaddr *macaddr,
+ const struct ipaddr *ip,
+ struct in_addr vtep_ip)
+{
+ struct wq_evpn_wrapper *w;
+
+ w = XCALLOC(MTYPE_WQ_WRAPPER, sizeof(struct wq_evpn_wrapper));
+
+ w->type = WQ_EVPN_WRAPPER_TYPE_REM_MACIP;
+ w->add_p = false;
+ w->vni = vni;
+ w->macaddr = *macaddr;
+ w->ip = *ip;
+ w->vtep_ip = vtep_ip;
+
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("%s: mac %pEA, vtep %pI4 enqueued", __func__,
+ macaddr, &vtep_ip);
+
+ return mq_add_handler(w, rib_meta_queue_evpn_add);
+}
+
+/*
+ * Enqueue remote VTEP address for processing
+ */
+int zebra_rib_queue_evpn_rem_vtep_add(vrf_id_t vrf_id, vni_t vni,
+ struct in_addr vtep_ip, int flood_control)
+{
+ struct wq_evpn_wrapper *w;
+
+ w = XCALLOC(MTYPE_WQ_WRAPPER, sizeof(struct wq_evpn_wrapper));
+
+ w->type = WQ_EVPN_WRAPPER_TYPE_REM_VTEP;
+ w->add_p = true;
+ w->vrf_id = vrf_id;
+ w->vni = vni;
+ w->vtep_ip = vtep_ip;
+ w->flags = flood_control;
+
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("%s: vrf %u, vtep %pI4 enqueued", __func__, vrf_id,
+ &vtep_ip);
+
+ return mq_add_handler(w, rib_meta_queue_evpn_add);
+}
+
+int zebra_rib_queue_evpn_rem_vtep_del(vrf_id_t vrf_id, vni_t vni,
+ struct in_addr vtep_ip)
+{
+ struct wq_evpn_wrapper *w;
+
+ w = XCALLOC(MTYPE_WQ_WRAPPER, sizeof(struct wq_evpn_wrapper));
+
+ w->type = WQ_EVPN_WRAPPER_TYPE_REM_VTEP;
+ w->add_p = false;
+ w->vrf_id = vrf_id;
+ w->vni = vni;
+ w->vtep_ip = vtep_ip;
+
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("%s: vrf %u, vtep %pI4 enqueued", __func__, vrf_id,
+ &vtep_ip);
+
+ return mq_add_handler(w, rib_meta_queue_evpn_add);
+}
+
+/* Clean up the EVPN meta-queue list */
+static void evpn_meta_queue_free(struct list *l)
+{
+ struct listnode *node;
+ struct wq_evpn_wrapper *w;
+
+ /* Free the node wrapper object, and the struct it wraps */
+ while ((node = listhead(l)) != NULL) {
+ w = node->data;
+ node->data = NULL;
+
+ XFREE(MTYPE_WQ_WRAPPER, w);
+
+ list_delete_node(l, node);
+ }
+}
+
/* Clean up the nhg meta-queue list */
static void nhg_meta_queue_free(struct list *l)
{
@@ -2656,7 +2967,7 @@ static void nhg_meta_queue_free(struct list *l)
else if (w->type == WQ_NHG_WRAPPER_TYPE_NHG)
zebra_nhg_free(w->u.nhe);
- XFREE(MTYPE_WQ_NHG_WRAPPER, w);
+ XFREE(MTYPE_WQ_WRAPPER, w);
list_delete_node(l, node);
}
@@ -2688,6 +2999,8 @@ void meta_queue_free(struct meta_queue *mq)
/* Some subqueues may need cleanup - nhgs for example */
if (i == route_info[ZEBRA_ROUTE_NHG].meta_q_map)
nhg_meta_queue_free(mq->subq[i]);
+ else if (i == META_QUEUE_EVPN)
+ evpn_meta_queue_free(mq->subq[i]);
list_delete(&mq->subq[i]);
}
@@ -2763,7 +3076,7 @@ rib_dest_t *zebra_rib_create_dest(struct route_node *rn)
* dest is created on-demand by rib_link() and is kept around at least
* as long as there are ribs hanging off it (@see rib_gc_dest()).
*
- * Refcounting (aka "locking" throughout the GNU Zebra and Quagga code):
+ * Refcounting (aka "locking" throughout the Zebra and FRR code):
*
* - route_nodes: refcounted by:
* - dest attached to route_node:
@@ -3518,7 +3831,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
&(tmp_nh->gate.ipv6),
sizeof(struct in6_addr));
}
- zebra_vxlan_evpn_vrf_route_del(re->vrf_id,
+ zebra_rib_queue_evpn_route_del(re->vrf_id,
&vtep_ip, p);
}
}
diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c
index 41d55c2e6c..017a4aae7f 100644
--- a/zebra/zebra_rnh.c
+++ b/zebra/zebra_rnh.c
@@ -598,8 +598,7 @@ static const int RNH_INVALID_NH_FLAGS = (NEXTHOP_FLAG_RECURSIVE |
NEXTHOP_FLAG_DUPLICATE |
NEXTHOP_FLAG_RNH_FILTERED);
-static bool rnh_nexthop_valid(const struct route_entry *re,
- const struct nexthop *nh)
+bool rnh_nexthop_valid(const struct route_entry *re, const struct nexthop *nh)
{
return (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)
&& CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE)
diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h
index 4897a6af30..24ee6d0bd9 100644
--- a/zebra/zebra_rnh.h
+++ b/zebra/zebra_rnh.h
@@ -64,6 +64,9 @@ extern void zebra_print_rnh_table(vrf_id_t vrfid, afi_t afi, struct vty *vty,
extern int rnh_resolve_via_default(struct zebra_vrf *zvrf, int family);
+extern bool rnh_nexthop_valid(const struct route_entry *re,
+ const struct nexthop *nh);
+
/* UI control to avoid notifications if backup nexthop status changes */
void rnh_set_hide_backups(bool hide_p);
bool rnh_get_hide_backups(void);
diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h
index 57dd0c20ad..f32f09850b 100644
--- a/zebra/zebra_vrf.h
+++ b/zebra/zebra_vrf.h
@@ -105,6 +105,7 @@ struct zebra_vrf {
/* MPLS Label to handle L3VPN <-> vrf popping */
mpls_label_t label[AFI_MAX];
+ uint8_t label_proto[AFI_MAX];
/* MPLS static LSP config table */
struct hash *slsp_table;
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index 2f3ea7475a..1660792221 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -366,8 +366,6 @@ static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty,
buf1, sizeof(buf1)));
json_object_int_add(json, "refCount",
rb_host_count(&zrmac->host_rb));
- json_object_int_add(json, "localSequence", zrmac->loc_seq);
- json_object_int_add(json, "remoteSequence", zrmac->rem_seq);
RB_FOREACH (hle, host_rb_tree_entry, &zrmac->host_rb)
json_object_array_add(
json_hosts,
@@ -3820,10 +3818,6 @@ void zebra_vxlan_remote_macip_del(ZAPI_HANDLER_ARGS)
uint16_t l = 0, ipa_len;
char buf1[INET6_ADDRSTRLEN];
- memset(&macaddr, 0, sizeof(struct ethaddr));
- memset(&ip, 0, sizeof(struct ipaddr));
- memset(&vtep_ip, 0, sizeof(struct in_addr));
-
s = msg;
while (l < hdr->length) {
@@ -3844,7 +3838,8 @@ void zebra_vxlan_remote_macip_del(ZAPI_HANDLER_ARGS)
ipaddr2str(&ip, buf1, sizeof(buf1)) : "",
&vtep_ip, zebra_route_string(client->proto));
- process_remote_macip_del(vni, &macaddr, ipa_len, &ip, vtep_ip);
+ /* Enqueue to workqueue for processing */
+ zebra_rib_queue_evpn_rem_macip_del(vni, &macaddr, &ip, vtep_ip);
}
stream_failure:
@@ -3870,10 +3865,6 @@ void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS)
esi_t esi;
char esi_buf[ESI_STR_LEN];
- memset(&macaddr, 0, sizeof(struct ethaddr));
- memset(&ip, 0, sizeof(struct ipaddr));
- memset(&vtep_ip, 0, sizeof(struct in_addr));
-
if (!EVPN_ENABLED(zvrf)) {
zlog_debug("EVPN not enabled, ignoring remote MACIP ADD");
return;
@@ -3882,6 +3873,7 @@ void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS)
s = msg;
while (l < hdr->length) {
+
int res_length = zebra_vxlan_remote_macip_helper(
true, s, &vni, &macaddr, &ipa_len, &ip, &vtep_ip,
&flags, &seq, &esi);
@@ -3907,8 +3899,9 @@ void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS)
zebra_route_string(client->proto));
}
- process_remote_macip_add(vni, &macaddr, ipa_len, &ip,
- flags, seq, vtep_ip, &esi);
+ /* Enqueue to workqueue for processing */
+ zebra_rib_queue_evpn_rem_macip_add(vni, &macaddr, &ip, flags,
+ seq, vtep_ip, &esi);
}
stream_failure:
@@ -4204,27 +4197,23 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
/*
* Handle message from client to delete a remote VTEP for an EVPN.
*/
-void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS)
+void zebra_vxlan_remote_vtep_del_zapi(ZAPI_HANDLER_ARGS)
{
struct stream *s;
unsigned short l = 0;
vni_t vni;
struct in_addr vtep_ip;
- zebra_evpn_t *zevpn;
- zebra_vtep_t *zvtep;
- struct interface *ifp;
- struct zebra_if *zif;
if (!is_evpn_enabled()) {
zlog_debug(
- "%s: EVPN is not enabled yet we have received a vtep del command",
+ "%s: EVPN is not enabled yet we have received a VTEP DEL msg",
__func__);
return;
}
if (!EVPN_ENABLED(zvrf)) {
- zlog_debug("Recv MACIP DEL for non-EVPN VRF %u",
- zvrf_id(zvrf));
+ zlog_debug("Recv VTEP DEL zapi for non-EVPN VRF %u",
+ zvrf_id(zvrf));
return;
}
@@ -4244,76 +4233,182 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS)
l += 4;
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Recv VTEP_DEL %pI4 VNI %u from %s",
+ zlog_debug("Recv VTEP DEL %pI4 VNI %u from %s",
&vtep_ip, vni,
zebra_route_string(client->proto));
- /* Locate VNI hash entry - expected to exist. */
- zevpn = zebra_evpn_lookup(vni);
- if (!zevpn) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "Failed to locate VNI hash upon remote VTEP DEL, VNI %u",
- vni);
- continue;
- }
+ /* Enqueue for processing */
+ zebra_rib_queue_evpn_rem_vtep_del(zvrf_id(zvrf), vni, vtep_ip);
+ }
- ifp = zevpn->vxlan_if;
- if (!ifp) {
+stream_failure:
+ return;
+}
+
+/*
+ * Handle message from client to delete a remote VTEP for an EVPN.
+ */
+void zebra_vxlan_remote_vtep_del(vrf_id_t vrf_id, vni_t vni,
+ struct in_addr vtep_ip)
+{
+ zebra_evpn_t *zevpn;
+ zebra_vtep_t *zvtep;
+ struct interface *ifp;
+ struct zebra_if *zif;
+ struct zebra_vrf *zvrf;
+
+ if (!is_evpn_enabled()) {
+ zlog_debug("%s: Can't process vtep del: EVPN is not enabled",
+ __func__);
+ return;
+ }
+
+ zvrf = zebra_vrf_lookup_by_id(vrf_id);
+ if (!zvrf)
+ return;
+
+ if (!EVPN_ENABLED(zvrf)) {
+ zlog_debug("Can't process VTEP DEL for non-EVPN VRF %u",
+ zvrf_id(zvrf));
+ return;
+ }
+
+ /* Locate VNI hash entry - expected to exist. */
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "VNI %u hash %p doesn't have intf upon remote VTEP DEL",
- zevpn->vni, zevpn);
- continue;
- }
- zif = ifp->info;
+ "Failed to locate VNI hash for remote VTEP DEL, VNI %u",
+ vni);
+ return;
+ }
- /* If down or not mapped to a bridge, we're done. */
- if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
- continue;
+ ifp = zevpn->vxlan_if;
+ if (!ifp) {
+ zlog_debug(
+ "VNI %u hash %p doesn't have intf upon remote VTEP DEL",
+ zevpn->vni, zevpn);
+ return;
+ }
+ zif = ifp->info;
- /* If the remote VTEP does not exist, there's nothing more to
- * do.
- * Otherwise, uninstall any remote MACs pointing to this VTEP
- * and
- * then, the VTEP entry itself and remove it.
- */
- zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip);
- if (!zvtep)
- continue;
+ /* If down or not mapped to a bridge, we're done. */
+ if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
+ return;
+
+ /* If the remote VTEP does not exist, there's nothing more to
+ * do.
+ * Otherwise, uninstall any remote MACs pointing to this VTEP
+ * and then, the VTEP entry itself and remove it.
+ */
+ zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip);
+ if (!zvtep)
+ return;
- zebra_evpn_vtep_uninstall(zevpn, &vtep_ip);
- zebra_evpn_vtep_del(zevpn, zvtep);
+ zebra_evpn_vtep_uninstall(zevpn, &vtep_ip);
+ zebra_evpn_vtep_del(zevpn, zvtep);
+}
+
+/*
+ * Handle message from client to add a remote VTEP for an EVPN.
+ */
+void zebra_vxlan_remote_vtep_add(vrf_id_t vrf_id, vni_t vni,
+ struct in_addr vtep_ip, int flood_control)
+{
+ zebra_evpn_t *zevpn;
+ struct interface *ifp;
+ struct zebra_if *zif;
+ zebra_vtep_t *zvtep;
+ struct zebra_vrf *zvrf;
+
+ if (!is_evpn_enabled()) {
+ zlog_debug("%s: EVPN not enabled: can't process a VTEP ADD",
+ __func__);
+ return;
}
-stream_failure:
- return;
+ zvrf = zebra_vrf_lookup_by_id(vrf_id);
+ if (!zvrf)
+ return;
+
+ if (!EVPN_ENABLED(zvrf)) {
+ zlog_debug("Can't process VTEP ADD for non-EVPN VRF %u",
+ zvrf_id(zvrf));
+ return;
+ }
+
+ /* Locate VNI hash entry - expected to exist. */
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
+ flog_err(
+ EC_ZEBRA_VTEP_ADD_FAILED,
+ "Failed to locate EVPN hash upon remote VTEP ADD, VNI %u",
+ vni);
+ return;
+ }
+
+ ifp = zevpn->vxlan_if;
+ if (!ifp) {
+ flog_err(
+ EC_ZEBRA_VTEP_ADD_FAILED,
+ "VNI %u hash %p doesn't have intf upon remote VTEP ADD",
+ zevpn->vni, zevpn);
+ return;
+ }
+
+ zif = ifp->info;
+
+ /* If down or not mapped to a bridge, we're done. */
+ if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
+ return;
+
+ zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip);
+ if (zvtep) {
+ /* If the remote VTEP already exists check if
+ * the flood mode has changed
+ */
+ if (zvtep->flood_control != flood_control) {
+ if (zvtep->flood_control == VXLAN_FLOOD_DISABLED)
+ /* old mode was head-end-replication but
+ * is no longer; get rid of the HER fdb
+ * entry installed before
+ */
+ zebra_evpn_vtep_uninstall(zevpn, &vtep_ip);
+ zvtep->flood_control = flood_control;
+ zebra_evpn_vtep_install(zevpn, zvtep);
+ }
+ } else {
+ zvtep = zebra_evpn_vtep_add(zevpn, &vtep_ip, flood_control);
+ if (zvtep)
+ zebra_evpn_vtep_install(zevpn, zvtep);
+ else
+ flog_err(EC_ZEBRA_VTEP_ADD_FAILED,
+ "Failed to add remote VTEP, VNI %u zevpn %p",
+ vni, zevpn);
+ }
}
/*
* Handle message from client to add a remote VTEP for an EVPN.
*/
-void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS)
+void zebra_vxlan_remote_vtep_add_zapi(ZAPI_HANDLER_ARGS)
{
struct stream *s;
unsigned short l = 0;
vni_t vni;
struct in_addr vtep_ip;
- zebra_evpn_t *zevpn;
- struct interface *ifp;
- struct zebra_if *zif;
int flood_control;
- zebra_vtep_t *zvtep;
if (!is_evpn_enabled()) {
zlog_debug(
- "%s: EVPN not enabled yet we received a vtep_add zapi call",
+ "%s: EVPN not enabled yet we received a VTEP ADD zapi msg",
__func__);
return;
}
if (!EVPN_ENABLED(zvrf)) {
- zlog_debug("Recv MACIP ADD for non-EVPN VRF %u",
- zvrf_id(zvrf));
+ zlog_debug("Recv VTEP ADD zapi for non-EVPN VRF %u",
+ zvrf_id(zvrf));
return;
}
@@ -4328,62 +4423,13 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS)
l += IPV4_MAX_BYTELEN + 4;
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Recv VTEP_ADD %pI4 VNI %u flood %d from %s",
- &vtep_ip, vni, flood_control,
- zebra_route_string(client->proto));
-
- /* Locate VNI hash entry - expected to exist. */
- zevpn = zebra_evpn_lookup(vni);
- if (!zevpn) {
- flog_err(
- EC_ZEBRA_VTEP_ADD_FAILED,
- "Failed to locate EVPN hash upon remote VTEP ADD, VNI %u",
- vni);
- continue;
- }
-
- ifp = zevpn->vxlan_if;
- if (!ifp) {
- flog_err(
- EC_ZEBRA_VTEP_ADD_FAILED,
- "VNI %u hash %p doesn't have intf upon remote VTEP ADD",
- zevpn->vni, zevpn);
- continue;
- }
-
- zif = ifp->info;
-
- /* If down or not mapped to a bridge, we're done. */
- if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
- continue;
+ zlog_debug("Recv VTEP ADD %pI4 VNI %u flood %d from %s",
+ &vtep_ip, vni, flood_control,
+ zebra_route_string(client->proto));
- zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip);
- if (zvtep) {
- /* If the remote VTEP already exists check if
- * the flood mode has changed
- */
- if (zvtep->flood_control != flood_control) {
- if (zvtep->flood_control
- == VXLAN_FLOOD_DISABLED)
- /* old mode was head-end-replication but
- * is no longer; get rid of the HER fdb
- * entry installed before
- */
- zebra_evpn_vtep_uninstall(zevpn,
- &vtep_ip);
- zvtep->flood_control = flood_control;
- zebra_evpn_vtep_install(zevpn, zvtep);
- }
- } else {
- zvtep = zebra_evpn_vtep_add(zevpn, &vtep_ip,
- flood_control);
- if (zvtep)
- zebra_evpn_vtep_install(zevpn, zvtep);
- else
- flog_err(EC_ZEBRA_VTEP_ADD_FAILED,
- "Failed to add remote VTEP, VNI %u zevpn %p",
- vni, zevpn);
- }
+ /* Enqueue for processing */
+ zebra_rib_queue_evpn_rem_vtep_add(zvrf_id(zvrf), vni, vtep_ip,
+ flood_control);
}
stream_failure:
@@ -4398,7 +4444,7 @@ stream_failure:
* 3. vrr interface (MACVLAN) associated to a SVI
* We advertise macip routes for an interface if it is associated to VxLan vlan
*/
-int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p,
+int zebra_vxlan_add_del_gw_macip(struct interface *ifp, const struct prefix *p,
int add)
{
struct ipaddr ip;
diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h
index 24de8ff04e..915e987b6b 100644
--- a/zebra/zebra_vxlan.h
+++ b/zebra/zebra_vxlan.h
@@ -72,8 +72,12 @@ is_vxlan_flooding_head_end(void)
/* ZAPI message handlers */
extern void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS);
extern void zebra_vxlan_remote_macip_del(ZAPI_HANDLER_ARGS);
-extern void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS);
-extern void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS);
+extern void zebra_vxlan_remote_vtep_add_zapi(ZAPI_HANDLER_ARGS);
+extern void zebra_vxlan_remote_vtep_del_zapi(ZAPI_HANDLER_ARGS);
+void zebra_vxlan_remote_vtep_add(vrf_id_t vrf_id, vni_t vni,
+ struct in_addr vtep_ip, int flood_control);
+extern void zebra_vxlan_remote_vtep_del(vrf_id_t vrf_id, vni_t vni,
+ struct in_addr vtep_ip);
extern void zebra_vxlan_flood_control(ZAPI_HANDLER_ARGS);
extern void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS);
extern void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS);
@@ -157,8 +161,8 @@ extern void zebra_vxlan_print_nh_all_l3vni(struct vty *vty, bool use_json);
extern void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni, bool use_json);
extern void zebra_vxlan_print_vrf_vni(struct vty *vty, struct zebra_vrf *zvrf,
json_object *json_vrfs);
-extern int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p,
- int add);
+extern int zebra_vxlan_add_del_gw_macip(struct interface *ifp,
+ const struct prefix *p, int add);
extern int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if);
extern int zebra_vxlan_svi_down(struct interface *ifp,
struct interface *link_if);
diff --git a/zebra/zserv.c b/zebra/zserv.c
index 1d94fcae6b..e4a48093f7 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -595,6 +595,8 @@ static void zserv_client_free(struct zserv *client)
close(client->sock);
if (DYNAMIC_CLIENT_GR_DISABLED(client)) {
+ zebra_mpls_client_cleanup_vrf_label(client->proto);
+
nroutes = rib_score_proto(client->proto,
client->instance);
zlog_notice(