summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am22
-rw-r--r--alpine/APKBUILD.in1
-rw-r--r--bfdd/dplane.c2
-rw-r--r--bgpd/bgp_aspath.c3
-rw-r--r--bgpd/bgp_attr.c39
-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_damp.c164
-rw-r--r--bgpd/bgp_damp.h11
-rw-r--r--bgpd/bgp_ecommunity.c12
-rw-r--r--bgpd/bgp_evpn.c20
-rw-r--r--bgpd/bgp_evpn_mh.c8
-rw-r--r--bgpd/bgp_fsm.c11
-rw-r--r--bgpd/bgp_lcommunity.c8
-rw-r--r--bgpd/bgp_mpath.c3
-rw-r--r--bgpd/bgp_mplsvpn.c4
-rw-r--r--bgpd/bgp_nht.c15
-rw-r--r--bgpd/bgp_nht.h3
-rw-r--r--bgpd/bgp_route.c200
-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.c60
-rw-r--r--bgpd/bgp_rpki.c8
-rw-r--r--bgpd/bgp_updgrp.c5
-rw-r--r--bgpd/bgp_updgrp_adv.c3
-rw-r--r--bgpd/bgp_updgrp_packet.c2
-rw-r--r--bgpd/bgp_vty.c45
-rw-r--r--bgpd/bgp_zebra.c4
-rw-r--r--bgpd/bgpd.c26
-rw-r--r--bgpd/bgpd.h1
-rw-r--r--bgpd/subdir.am6
-rw-r--r--configure.ac25
-rw-r--r--debian/README.Debian8
-rw-r--r--debian/control1
-rwxr-xr-xdebian/rules13
-rw-r--r--doc/developer/building-frr-for-archlinux.rst4
-rw-r--r--doc/developer/building-frr-for-centos7.rst3
-rw-r--r--doc/developer/building-frr-for-centos8.rst3
-rw-r--r--doc/developer/building-frr-for-debian8.rst2
-rw-r--r--doc/developer/building-frr-for-debian9.rst2
-rw-r--r--doc/developer/building-frr-for-fedora.rst2
-rw-r--r--doc/developer/building-frr-for-opensuse.rst2
-rw-r--r--doc/developer/building-frr-for-ubuntu1604.rst4
-rw-r--r--doc/developer/building-frr-for-ubuntu1804.rst4
-rw-r--r--doc/developer/building-frr-for-ubuntu2004.rst4
-rw-r--r--doc/developer/include-compile.rst5
-rw-r--r--doc/developer/packaging-debian.rst10
-rw-r--r--doc/developer/packaging-redhat.rst4
-rw-r--r--doc/developer/workflow.rst98
-rw-r--r--doc/user/bgp.rst65
-rw-r--r--doc/user/filter.rst9
-rw-r--r--doc/user/installation.rst11
-rw-r--r--doc/user/ospf6d.rst142
-rw-r--r--doc/user/ospfd.rst24
-rw-r--r--doc/user/pbr.rst15
-rw-r--r--doc/user/pim.rst8
-rw-r--r--docker/centos-7/Dockerfile2
-rw-r--r--docker/centos-8/Dockerfile2
-rw-r--r--docker/ubuntu18-ci/Dockerfile4
-rw-r--r--docker/ubuntu20-ci/Dockerfile4
-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/buffer.c24
-rw-r--r--lib/command.c3
-rw-r--r--lib/command.h2
-rw-r--r--lib/csv.c3
-rw-r--r--lib/frr_zmq.c24
-rw-r--r--lib/libfrr.c6
-rw-r--r--lib/link_state.c12
-rw-r--r--lib/network.c3
-rw-r--r--lib/ntop.c14
-rw-r--r--lib/pbr.h6
-rw-r--r--lib/plist.c66
-rw-r--r--lib/plist.h15
-rw-r--r--lib/prefix.c3
-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/sockopt.c16
-rw-r--r--lib/stream.c8
-rw-r--r--lib/subdir.am17
-rw-r--r--lib/systemd.c173
-rw-r--r--lib/systemd.h10
-rw-r--r--lib/zclient.c6
-rw-r--r--mlag/subdir.am2
-rw-r--r--ospf6d/ospf6_abr.c27
-rw-r--r--ospf6d/ospf6_area.c2
-rw-r--r--ospf6d/ospf6_asbr.c1276
-rw-r--r--ospf6d/ospf6_asbr.h73
-rw-r--r--ospf6d/ospf6_flood.c66
-rw-r--r--ospf6d/ospf6_flood.h3
-rw-r--r--ospf6d/ospf6_interface.c98
-rw-r--r--ospf6d/ospf6_interface.h1
-rw-r--r--ospf6d/ospf6_intra.c46
-rw-r--r--ospf6d/ospf6_lsa.c35
-rw-r--r--ospf6d/ospf6_lsa.h5
-rw-r--r--ospf6d/ospf6_lsdb.c23
-rw-r--r--ospf6d/ospf6_message.c59
-rw-r--r--ospf6d/ospf6_nssa.c57
-rw-r--r--ospf6d/ospf6_route.c94
-rw-r--r--ospf6d/ospf6_route.h86
-rw-r--r--ospf6d/ospf6_spf.c30
-rw-r--r--ospf6d/ospf6_top.c514
-rw-r--r--ospf6d/ospf6_top.h17
-rw-r--r--ospf6d/ospf6_zebra.c33
-rw-r--r--ospf6d/ospf6d.h4
-rw-r--r--ospf6d/subdir.am3
-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_network.c2
-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.c34
-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/path_pcep_config.c2
-rw-r--r--pathd/path_pcep_pcc.c53
-rw-r--r--pathd/subdir.am2
-rw-r--r--pbrd/pbr_map.h11
-rw-r--r--pbrd/pbr_vty.c92
-rw-r--r--pbrd/pbr_zebra.c5
-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_msdp.c1
-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--redhat/frr.spec.in5
-rw-r--r--ripd/subdir.am2
-rw-r--r--staticd/static_routes.c7
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/bgpd/test_aspath.c58
-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
-rwxr-xr-xtests/topotests/bgp-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py3
-rw-r--r--tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py2
-rw-r--r--tests/topotests/bgp_aspath_zero/__init__.py0
-rw-r--r--tests/topotests/bgp_aspath_zero/exabgp.env53
-rw-r--r--tests/topotests/bgp_aspath_zero/peer1/exabgp.cfg17
-rw-r--r--tests/topotests/bgp_aspath_zero/r1/bgpd.conf6
-rw-r--r--tests/topotests/bgp_aspath_zero/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_aspath_zero/test_bgp_aspath_zero.py121
-rw-r--r--tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py24
-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.py46
-rw-r--r--tests/topotests/bgp_community_change_update/test_bgp_community_change_update.py2
-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_conditional_advertisement/test_bgp_conditional_advertisement.py2
-rw-r--r--tests/topotests/bgp_default_afi_safi/test_bgp-default-afi-safi.py2
-rw-r--r--tests/topotests/bgp_default_route/r2/bgpd.conf3
-rw-r--r--tests/topotests/bgp_default_route/test_bgp_default-originate.py29
-rw-r--r--tests/topotests/bgp_default_route_route_map_match/test_bgp_default-originate_route-map_match.py2
-rw-r--r--tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py2
-rw-r--r--tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py3
-rw-r--r--tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py2
-rw-r--r--tests/topotests/bgp_distance_change/test_bgp_distance_change.py2
-rw-r--r--tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/test_bgp-ebgp-common-subnet-nexthop-unchanged.py2
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py2
-rw-r--r--tests/topotests/bgp_evpn_mh/test_evpn_mh.py24
-rw-r--r--tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py1
-rwxr-xr-xtests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py8
-rw-r--r--tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py3
-rw-r--r--tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py3
-rw-r--r--tests/topotests/bgp_gshut/test_bgp_gshut.py2
-rw-r--r--tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py3
-rw-r--r--tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py3
-rw-r--r--tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py3
-rw-r--r--tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py3
-rw-r--r--tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py3
-rwxr-xr-xtests/topotests/bgp_listen_on_multiple_addresses/test_bgp_listen_on_multiple_addresses.py6
-rw-r--r--tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py2
-rw-r--r--tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py2
-rw-r--r--tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py2
-rw-r--r--tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py1
-rw-r--r--tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py4
-rw-r--r--tests/topotests/bgp_path_attributes_topo1/test_bgp_path_attributes.py3
-rwxr-xr-xtests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py2
-rw-r--r--tests/topotests/bgp_prefix_list_topo1/test_prefix_lists.py3
-rw-r--r--tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py2
-rwxr-xr-xtests/topotests/bgp_prefix_sid2/test_bgp_prefix_sid2.py2
-rw-r--r--tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py2
-rw-r--r--tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py3
-rw-r--r--tests/topotests/bgp_route_map/test_route_map_topo1.py3
-rw-r--r--tests/topotests/bgp_route_map/test_route_map_topo2.py3
-rw-r--r--tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py3
-rw-r--r--tests/topotests/bgp_set_local_preference_add_subtract/test_bgp_set_local-preference_add_subtract.py2
-rwxr-xr-xtests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py2
-rw-r--r--tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py2
-rw-r--r--tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py2
-rw-r--r--tests/topotests/bgp_update_delay/test_bgp_update_delay.py2
-rw-r--r--tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py3
-rw-r--r--tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py3
-rw-r--r--tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py2
-rw-r--r--tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py3
-rw-r--r--tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py2
-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_pim_1/test_evpn_pim_topo1.py3
-rw-r--r--tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json191
-rw-r--r--tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py3
-rw-r--r--tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py3
-rwxr-xr-xtests/topotests/isis_lsp_bits_topo1/test_isis_lsp_bits_topo1.py3
-rwxr-xr-xtests/topotests/isis_rlfa_topo1/test_isis_rlfa_topo1.py2
-rwxr-xr-xtests/topotests/isis_snmp/test_isis_snmp.py2
-rw-r--r--tests/topotests/ldp_snmp/test_ldp_snmp_topo1.py1
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/test_ldp_sync_isis_topo1.py2
-rw-r--r--tests/topotests/lib/bgp.py412
-rw-r--r--tests/topotests/lib/common_config.py128
-rw-r--r--tests/topotests/lib/ospf.py865
-rwxr-xr-xtests/topotests/lib/scapy_sendpkt.py61
-rw-r--r--tests/topotests/lib/topogen.py4
-rw-r--r--tests/topotests/lib/topojson.py18
-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/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py3
-rw-r--r--tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py3
-rwxr-xr-xtests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py3
-rwxr-xr-xtests/topotests/multicast_pim_sm_topo2/test_multicast_pim_sm_topo2.py3
-rwxr-xr-xtests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp.py3
-rw-r--r--tests/topotests/nhrp_topo/test_nhrp_topo.py4
-rw-r--r--tests/topotests/ospf6_topo1/test_ospf6_topo1.py3
-rwxr-xr-xtests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py3
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py3
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py3
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_authentication.py3
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py3
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py3
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_lan.py3
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_nssa.py3
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py3
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py3
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py3
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_single_area.py1
-rw-r--r--tests/topotests/ospf_dual_stack/test_ospf_dual_stack.py3
-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/ospf_suppress_fa/test_ospf_suppress_fa.py2
-rw-r--r--tests/topotests/ospf_tilfa_topo1/test_ospf_tilfa_topo1.py2
-rw-r--r--tests/topotests/ospf_topo2/test_ospf_topo2.py2
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_asbr_summary_topo1.json198
-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_asbr_summary_topo1.py1928
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py523
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py875
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py231
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py5
-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--tests/topotests/route_scale/test_route_scale.py3
-rwxr-xr-xtests/topotests/simple_snmp_test/test_simple_snmp.py10
-rwxr-xr-xtests/topotests/srv6_locator/test_srv6_locator.py2
-rw-r--r--tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py3
-rw-r--r--tests/topotests/zebra_netlink/test_zebra_netlink.py3
-rw-r--r--tests/topotests/zebra_opaque/test_zebra_opaque.py2
-rw-r--r--tests/topotests/zebra_rib/test_zebra_rib.py8
-rwxr-xr-xtests/topotests/zebra_seg6_route/test_zebra_seg6_route.py2
-rwxr-xr-xtests/topotests/zebra_seg6local_route/test_zebra_seg6local_route.py2
-rw-r--r--tools/coccinelle/hash_compare_null_values_check.cocci20
-rw-r--r--tools/coccinelle/xcalloc-xmalloc.cocci17
-rwxr-xr-xtools/frr-reload.py46
-rw-r--r--vtysh/vtysh.c44
-rw-r--r--watchfrr/watchfrr.c10
-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_netlink.c6
-rw-r--r--zebra/kernel_netlink.h2
-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/rule_netlink.c34
-rw-r--r--zebra/subdir.am12
-rw-r--r--zebra/zapi_msg.c24
-rw-r--r--zebra/zebra_dplane.c26
-rw-r--r--zebra/zebra_dplane.h2
-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_fpm.c4
-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_pbr.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_vty.c14
-rw-r--r--zebra/zebra_vxlan.c282
-rw-r--r--zebra/zebra_vxlan.h12
-rw-r--r--zebra/zserv.c2
457 files changed, 18572 insertions, 2384 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/alpine/APKBUILD.in b/alpine/APKBUILD.in
index dfedf0b52b..e6776cb3a2 100644
--- a/alpine/APKBUILD.in
+++ b/alpine/APKBUILD.in
@@ -41,7 +41,6 @@ build() {
--sysconfdir=$_sysconfdir \
--libdir=$_libdir \
--localstatedir=$_localstatedir \
- --enable-systemd=no \
--enable-rpki \
--enable-vtysh \
--enable-multipath=64 \
diff --git a/bfdd/dplane.c b/bfdd/dplane.c
index 6fb301d46a..4b7f9ba7ac 100644
--- a/bfdd/dplane.c
+++ b/bfdd/dplane.c
@@ -651,8 +651,6 @@ static struct bfd_dplane_ctx *bfd_dplane_ctx_new(int sock)
struct bfd_dplane_ctx *bdc;
bdc = XCALLOC(MTYPE_BFDD_DPLANE_CTX, sizeof(*bdc));
- if (bdc == NULL)
- return NULL;
bdc->sock = sock;
bdc->inbuf = stream_new(BFD_DPLANE_CLIENT_BUF_SIZE);
diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c
index 25109e030b..3c67017dc7 100644
--- a/bgpd/bgp_aspath.c
+++ b/bgpd/bgp_aspath.c
@@ -193,7 +193,8 @@ static struct assegment *assegment_prepend_asns(struct assegment *seg,
if (num >= AS_SEGMENT_MAX)
return seg; /* we don't do huge prepends */
- if ((newas = assegment_data_new(seg->length + num)) == NULL)
+ newas = assegment_data_new(seg->length + num);
+ if (newas == NULL)
return seg;
for (i = 0; i < num; i++)
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 2f0751a5f0..adf408220e 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -1509,16 +1509,6 @@ static int bgp_attr_aspath(struct bgp_attr_parser_args *args)
0);
}
- /* Codification of AS 0 Processing */
- if (aspath_check_as_zero(attr->aspath)) {
- flog_err(
- EC_BGP_ATTR_MAL_AS_PATH,
- "Malformed AS path, AS number is 0 in the path from %s",
- peer->host);
- return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_MAL_AS_PATH,
- 0);
- }
-
/* Set aspath attribute flag. */
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS_PATH);
@@ -1558,6 +1548,15 @@ static bgp_attr_parse_ret_t bgp_attr_aspath_check(struct peer *const peer,
}
}
+ /* Codification of AS 0 Processing */
+ if (peer->sort == BGP_PEER_EBGP && aspath_check_as_zero(attr->aspath)) {
+ flog_err(
+ EC_BGP_ATTR_MAL_AS_PATH,
+ "Malformed AS path, AS number is 0 in the path from %s",
+ peer->host);
+ return BGP_ATTR_PARSE_WITHDRAW;
+ }
+
/* local-as prepend */
if (peer->change_local_as
&& !CHECK_FLAG(peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND)) {
@@ -1590,16 +1589,6 @@ static int bgp_attr_as4_path(struct bgp_attr_parser_args *args,
0);
}
- /* Codification of AS 0 Processing */
- if (aspath_check_as_zero(*as4_path)) {
- flog_err(
- EC_BGP_ATTR_MAL_AS_PATH,
- "Malformed AS path, AS number is 0 in the path from %s",
- peer->host);
- return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_MAL_AS_PATH,
- 0);
- }
-
/* Set aspath attribute flag. */
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS4_PATH);
@@ -2926,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;
@@ -3317,7 +3303,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
}
/* Check all mandatory well-known attributes are present */
- if ((ret = bgp_attr_check(peer, attr)) < 0)
+ ret = bgp_attr_check(peer, attr);
+ if (ret < 0)
goto done;
/*
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_damp.c b/bgpd/bgp_damp.c
index 2a372c0ba4..fca5089da5 100644
--- a/bgpd/bgp_damp.c
+++ b/bgpd/bgp_damp.c
@@ -40,72 +40,33 @@
static void bgp_reuselist_add(struct reuselist *list,
struct bgp_damp_info *info)
{
- struct reuselist_node *new_node;
-
assert(info);
- new_node = XCALLOC(MTYPE_BGP_DAMP_REUSELIST, sizeof(*new_node));
- new_node->info = info;
- SLIST_INSERT_HEAD(list, new_node, entry);
+ LIST_INSERT_HEAD(list, info, entry);
}
static void bgp_reuselist_del(struct reuselist *list,
- struct reuselist_node **node)
+ struct bgp_damp_info *info)
{
- if ((*node) == NULL)
- return;
- assert(list && node && *node);
- SLIST_REMOVE(list, (*node), reuselist_node, entry);
- XFREE(MTYPE_BGP_DAMP_REUSELIST, (*node));
- *node = NULL;
+ assert(info);
+ LIST_REMOVE(info, entry);
}
static void bgp_reuselist_switch(struct reuselist *source,
- struct reuselist_node *node,
+ struct bgp_damp_info *info,
struct reuselist *target)
{
- assert(source && target && node);
- SLIST_REMOVE(source, node, reuselist_node, entry);
- SLIST_INSERT_HEAD(target, node, entry);
-}
-
-static void bgp_reuselist_free(struct reuselist *list)
-{
- struct reuselist_node *rn;
-
- assert(list);
- while ((rn = SLIST_FIRST(list)) != NULL)
- bgp_reuselist_del(list, &rn);
-}
-
-static struct reuselist_node *bgp_reuselist_find(struct reuselist *list,
- struct bgp_damp_info *info)
-{
- struct reuselist_node *rn;
-
- assert(list && info);
- SLIST_FOREACH (rn, list, entry) {
- if (rn->info == info)
- return rn;
- }
- return NULL;
+ assert(source && target && info);
+ LIST_REMOVE(info, entry);
+ LIST_INSERT_HEAD(target, info, entry);
}
static void bgp_damp_info_unclaim(struct bgp_damp_info *bdi)
{
- struct reuselist_node *node;
-
assert(bdi && bdi->config);
- if (bdi->index == BGP_DAMP_NO_REUSE_LIST_INDEX) {
- node = bgp_reuselist_find(&bdi->config->no_reuse_list, bdi);
- if (node)
- bgp_reuselist_del(&bdi->config->no_reuse_list, &node);
- } else {
- node = bgp_reuselist_find(&bdi->config->reuse_list[bdi->index],
- bdi);
- if (node)
- bgp_reuselist_del(&bdi->config->reuse_list[bdi->index],
- &node);
- }
+ if (bdi->index == BGP_DAMP_NO_REUSE_LIST_INDEX)
+ bgp_reuselist_del(&bdi->config->no_reuse_list, bdi);
+ else
+ bgp_reuselist_del(&bdi->config->reuse_list[bdi->index], bdi);
bdi->config = NULL;
}
@@ -174,16 +135,9 @@ static void bgp_reuse_list_add(struct bgp_damp_info *bdi,
}
/* Delete BGP dampening information from reuse list. */
-static void bgp_reuse_list_delete(struct bgp_damp_info *bdi,
- struct bgp_damp_config *bdc)
+static void bgp_reuse_list_delete(struct bgp_damp_info *bdi)
{
- struct reuselist *list;
- struct reuselist_node *rn;
-
- list = &bdc->reuse_list[bdi->index];
- rn = bgp_reuselist_find(list, bdi);
bgp_damp_info_unclaim(bdi);
- bgp_reuselist_del(list, &rn);
}
static void bgp_no_reuse_list_add(struct bgp_damp_info *bdi,
@@ -194,19 +148,9 @@ static void bgp_no_reuse_list_add(struct bgp_damp_info *bdi,
bgp_reuselist_add(&bdc->no_reuse_list, bdi);
}
-static void bgp_no_reuse_list_delete(struct bgp_damp_info *bdi,
- struct bgp_damp_config *bdc)
+static void bgp_no_reuse_list_delete(struct bgp_damp_info *bdi)
{
- struct reuselist_node *rn;
-
- assert(bdc && bdi);
- if (bdi->config == NULL) {
- bgp_damp_info_unclaim(bdi);
- return;
- }
- bdi->config = NULL;
- rn = bgp_reuselist_find(&bdc->no_reuse_list, bdi);
- bgp_reuselist_del(&bdc->no_reuse_list, &rn);
+ bgp_damp_info_unclaim(bdi);
}
/* Return decayed penalty value. */
@@ -232,7 +176,6 @@ static int bgp_reuse_timer(struct thread *t)
struct bgp_damp_config *bdc = THREAD_ARG(t);
struct bgp_damp_info *bdi;
struct reuselist plist;
- struct reuselist_node *node;
struct bgp *bgp;
time_t t_now, t_diff;
@@ -245,7 +188,7 @@ static int bgp_reuse_timer(struct thread *t)
* list head entry. */
assert(bdc->reuse_offset < bdc->reuse_list_size);
plist = bdc->reuse_list[bdc->reuse_offset];
- SLIST_INIT(&bdc->reuse_list[bdc->reuse_offset]);
+ LIST_INIT(&bdc->reuse_list[bdc->reuse_offset]);
/* 2. set offset = modulo reuse-list-size ( offset + 1 ), thereby
rotating the circular queue of list-heads. */
@@ -253,8 +196,7 @@ static int bgp_reuse_timer(struct thread *t)
assert(bdc->reuse_offset < bdc->reuse_list_size);
/* 3. if ( the saved list head pointer is non-empty ) */
- while ((node = SLIST_FIRST(&plist)) != NULL) {
- bdi = node->info;
+ while ((bdi = LIST_FIRST(&plist)) != NULL) {
bgp = bdi->path->peer->bgp;
/* Set t-diff = t-now - t-updated. */
@@ -285,25 +227,22 @@ static int bgp_reuse_timer(struct thread *t)
}
if (bdi->penalty <= bdc->reuse_limit / 2.0) {
- bgp_damp_info_free(&bdi, bdc, 1, bdi->afi,
- bdi->safi);
- bgp_reuselist_del(&plist, &node);
+ bgp_damp_info_free(bdi, 1);
} else {
- node->info->index =
- BGP_DAMP_NO_REUSE_LIST_INDEX;
- bgp_reuselist_switch(&plist, node,
+ bdi->index = BGP_DAMP_NO_REUSE_LIST_INDEX;
+ bgp_reuselist_switch(&plist, bdi,
&bdc->no_reuse_list);
}
} else {
/* Re-insert into another list (See RFC2439 Section
* 4.8.6). */
bdi->index = bgp_reuse_index(bdi->penalty, bdc);
- bgp_reuselist_switch(&plist, node,
+ bgp_reuselist_switch(&plist, bdi,
&bdc->reuse_list[bdi->index]);
}
}
- assert(SLIST_EMPTY(&plist));
+ assert(LIST_EMPTY(&plist));
return 0;
}
@@ -349,7 +288,14 @@ int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_dest *dest,
(bgp_path_info_extra_get(path))->damp_info = bdi;
bgp_no_reuse_list_add(bdi, bdc);
} else {
- bgp_damp_info_claim(bdi, bdc);
+ if (bdi->config != bdc) {
+ bgp_damp_info_claim(bdi, bdc);
+ if (bdi->index == BGP_DAMP_NO_REUSE_LIST_INDEX)
+ bgp_reuselist_add(&bdc->no_reuse_list, bdi);
+ else
+ bgp_reuselist_add(&bdc->reuse_list[bdi->index],
+ bdi);
+ }
last_penalty = bdi->penalty;
/* 1. Set t-diff = t-now - t-updated. */
@@ -376,7 +322,7 @@ int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_dest *dest,
if (CHECK_FLAG(bdi->path->flags, BGP_PATH_DAMPED)) {
/* If decay rate isn't equal to 0, reinsert brn. */
if (bdi->penalty != last_penalty) {
- bgp_reuse_list_delete(bdi, bdc);
+ bgp_reuse_list_delete(bdi);
bgp_reuse_list_add(bdi, bdc);
}
return BGP_DAMP_SUPPRESSED;
@@ -387,7 +333,7 @@ int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_dest *dest,
if (bdi->penalty >= bdc->suppress_value) {
bgp_path_info_set_flag(dest, path, BGP_PATH_DAMPED);
bdi->suppress_time = t_now;
- bgp_no_reuse_list_delete(bdi, bdc);
+ bgp_no_reuse_list_delete(bdi);
bgp_reuse_list_add(bdi, bdc);
}
return BGP_DAMP_USED;
@@ -420,7 +366,7 @@ int bgp_damp_update(struct bgp_path_info *path, struct bgp_dest *dest,
else if (CHECK_FLAG(bdi->path->flags, BGP_PATH_DAMPED)
&& (bdi->penalty < bdc->reuse_limit)) {
bgp_path_info_unset_flag(dest, path, BGP_PATH_DAMPED);
- bgp_reuse_list_delete(bdi, bdc);
+ bgp_reuse_list_delete(bdi);
bgp_no_reuse_list_add(bdi, bdc);
bdi->suppress_time = 0;
status = BGP_DAMP_USED;
@@ -429,29 +375,25 @@ int bgp_damp_update(struct bgp_path_info *path, struct bgp_dest *dest,
if (bdi->penalty > bdc->reuse_limit / 2.0)
bdi->t_updated = t_now;
- else {
- bgp_damp_info_unclaim(bdi);
- bgp_damp_info_free(&bdi, bdc, 0, afi, safi);
- }
+ else
+ bgp_damp_info_free(bdi, 0);
return status;
}
-void bgp_damp_info_free(struct bgp_damp_info **bdi, struct bgp_damp_config *bdc,
- int withdraw, afi_t afi, safi_t safi)
+void bgp_damp_info_free(struct bgp_damp_info *bdi, int withdraw)
{
- assert(bdc && bdi && *bdi);
+ assert(bdi);
- if ((*bdi)->path == NULL) {
- XFREE(MTYPE_BGP_DAMP_INFO, (*bdi));
- return;
- }
+ bgp_damp_info_unclaim(bdi);
- (*bdi)->path->extra->damp_info = NULL;
- bgp_path_info_unset_flag((*bdi)->dest, (*bdi)->path,
+ bdi->path->extra->damp_info = NULL;
+ bgp_path_info_unset_flag(bdi->dest, bdi->path,
BGP_PATH_HISTORY | BGP_PATH_DAMPED);
- if ((*bdi)->lastrecord == BGP_RECORD_WITHDRAW && withdraw)
- bgp_path_info_delete((*bdi)->dest, (*bdi)->path);
+ if (bdi->lastrecord == BGP_RECORD_WITHDRAW && withdraw)
+ bgp_path_info_delete(bdi->dest, bdi->path);
+
+ XFREE(MTYPE_BGP_DAMP_INFO, bdi);
}
static void bgp_damp_parameter_set(int hlife, int reuse, int sup, int maxsup,
@@ -548,15 +490,13 @@ void bgp_damp_info_clean(struct bgp *bgp, struct bgp_damp_config *bdc,
afi_t afi, safi_t safi)
{
struct bgp_damp_info *bdi;
- struct reuselist_node *rn;
struct reuselist *list;
unsigned int i;
bdc->reuse_offset = 0;
for (i = 0; i < bdc->reuse_list_size; ++i) {
list = &bdc->reuse_list[i];
- while ((rn = SLIST_FIRST(list)) != NULL) {
- bdi = rn->info;
+ while ((bdi = LIST_FIRST(list)) != NULL) {
if (bdi->lastrecord == BGP_RECORD_UPDATE) {
bgp_aggregate_increment(bgp, &bdi->dest->p,
bdi->path, bdi->afi,
@@ -564,16 +504,12 @@ void bgp_damp_info_clean(struct bgp *bgp, struct bgp_damp_config *bdc,
bgp_process(bgp, bdi->dest, bdi->afi,
bdi->safi);
}
- bgp_reuselist_del(list, &rn);
- bgp_damp_info_free(&bdi, bdc, 1, afi, safi);
+ bgp_damp_info_free(bdi, 1);
}
}
- while ((rn = SLIST_FIRST(&bdc->no_reuse_list)) != NULL) {
- bdi = rn->info;
- bgp_reuselist_del(&bdc->no_reuse_list, &rn);
- bgp_damp_info_free(&bdi, bdc, 1, afi, safi);
- }
+ while ((bdi = LIST_FIRST(&bdc->no_reuse_list)) != NULL)
+ bgp_damp_info_free(bdi, 1);
/* Free decay array */
XFREE(MTYPE_BGP_DAMP_ARRAY, bdc->decay_array);
@@ -583,10 +519,6 @@ void bgp_damp_info_clean(struct bgp *bgp, struct bgp_damp_config *bdc,
XFREE(MTYPE_BGP_DAMP_ARRAY, bdc->reuse_index);
bdc->reuse_index_size = 0;
- /* Free reuse list array. */
- for (i = 0; i < bdc->reuse_list_size; ++i)
- bgp_reuselist_free(&bdc->reuse_list[i]);
-
XFREE(MTYPE_BGP_DAMP_ARRAY, bdc->reuse_list);
bdc->reuse_list_size = 0;
diff --git a/bgpd/bgp_damp.h b/bgpd/bgp_damp.h
index c03a0cc5c9..d238a7a340 100644
--- a/bgpd/bgp_damp.h
+++ b/bgpd/bgp_damp.h
@@ -61,14 +61,11 @@ struct bgp_damp_info {
afi_t afi;
safi_t safi;
-};
-struct reuselist_node {
- SLIST_ENTRY(reuselist_node) entry;
- struct bgp_damp_info *info;
+ LIST_ENTRY(bgp_damp_info) entry;
};
-SLIST_HEAD(reuselist, reuselist_node);
+LIST_HEAD(reuselist, bgp_damp_info);
/* Specified parameter set configuration. */
struct bgp_damp_config {
@@ -148,9 +145,7 @@ extern int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_dest *dest,
afi_t afi, safi_t safi, int attr_change);
extern int bgp_damp_update(struct bgp_path_info *path, struct bgp_dest *dest,
afi_t afi, safi_t saff);
-extern void bgp_damp_info_free(struct bgp_damp_info **path,
- struct bgp_damp_config *bdc, int withdraw,
- afi_t afi, safi_t safi);
+extern void bgp_damp_info_free(struct bgp_damp_info *bdi, int withdraw);
extern void bgp_damp_info_clean(struct bgp *bgp, struct bgp_damp_config *bdc,
afi_t afi, safi_t safi);
extern void bgp_damp_config_clean(struct bgp_damp_config *bdc);
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_evpn_mh.c b/bgpd/bgp_evpn_mh.c
index 4b90937f62..34094a0bde 100644
--- a/bgpd/bgp_evpn_mh.c
+++ b/bgpd/bgp_evpn_mh.c
@@ -1311,7 +1311,7 @@ static void bgp_evpn_es_vtep_re_eval_active(struct bgp *bgp,
bool old_active;
bool new_active;
- old_active = !!CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE);
+ old_active = CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE);
/* currently we need an active EVI reference to use the VTEP as
* a nexthop. this may change...
*/
@@ -1320,7 +1320,7 @@ static void bgp_evpn_es_vtep_re_eval_active(struct bgp *bgp,
else
UNSET_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE);
- new_active = !!CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE);
+ new_active = CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE);
if ((old_active != new_active) || (new_active && param_change)) {
@@ -3119,7 +3119,7 @@ static void bgp_evpn_es_evi_vtep_re_eval_active(struct bgp *bgp,
bool new_active;
uint32_t ead_activity_flags;
- old_active = !!CHECK_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE);
+ old_active = CHECK_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE);
if (bgp_mh_info->ead_evi_rx)
/* Both EAD-per-ES and EAD-per-EVI routes must be rxed from a PE
@@ -3135,7 +3135,7 @@ static void bgp_evpn_es_evi_vtep_re_eval_active(struct bgp *bgp,
else
UNSET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE);
- new_active = !!CHECK_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE);
+ new_active = CHECK_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE);
if (old_active == new_active)
return;
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index 4cc096d8e7..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)
@@ -256,10 +256,7 @@ static struct peer *peer_xfer_conn(struct peer *from_peer)
from_peer->last_major_event = last_maj_evt;
peer->remote_id = from_peer->remote_id;
peer->last_reset = from_peer->last_reset;
-
- 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);
+ peer->max_packet_size = from_peer->max_packet_size;
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_mpath.c b/bgpd/bgp_mpath.c
index d5fce115de..8127428bc7 100644
--- a/bgpd/bgp_mpath.c
+++ b/bgpd/bgp_mpath.c
@@ -411,7 +411,8 @@ static void bgp_path_info_mpath_lb_update(struct bgp_path_info *path, bool set,
{
struct bgp_path_info_mpath *mpath;
- if ((mpath = path->mpath) == NULL) {
+ mpath = path->mpath;
+ if (mpath == NULL) {
if (!set || (cum_bw == 0 && !all_paths_lb))
return;
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 6f7424c5ea..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) {
/*
@@ -1053,7 +1064,7 @@ void evaluate_paths(struct bgp_nexthop_cache *bnc)
|| path->attr->srte_color != 0)
SET_FLAG(path->flags, BGP_PATH_IGP_CHANGED);
- path_valid = !!CHECK_FLAG(path->flags, BGP_PATH_VALID);
+ path_valid = CHECK_FLAG(path->flags, BGP_PATH_VALID);
if (path_valid != bnc_is_valid_nexthop) {
if (path_valid) {
/* No longer valid, clear flag; also for EVPN
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 ab877574fa..4299ea3525 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -222,7 +222,10 @@ void bgp_path_info_extra_free(struct bgp_path_info_extra **extra)
e = *extra;
+ if (e->damp_info)
+ bgp_damp_info_free(e->damp_info, 0);
e->damp_info = NULL;
+
if (e->parent) {
struct bgp_path_info *bpi = (struct bgp_path_info *)e->parent;
@@ -3663,7 +3666,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 +4106,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 +4248,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 +5687,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 +5739,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 +6057,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 +6065,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 +9524,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 +9543,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));
-
- /* Print attribute */
- attr = path->attr;
+ use_json, NULL));
- /* 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 +10865,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 +10880,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;
@@ -11643,7 +11630,8 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp,
if (!table)
continue;
- if ((rm = bgp_node_match(table, &match)) == NULL)
+ rm = bgp_node_match(table, &match);
+ if (rm == NULL)
continue;
const struct prefix *rm_p = bgp_dest_get_prefix(rm);
@@ -11735,7 +11723,8 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp,
json_object_free(json_paths);
}
} else {
- if ((dest = bgp_node_match(rib, &match)) != NULL) {
+ dest = bgp_node_match(rib, &match);
+ if (dest != NULL) {
const struct prefix *dest_p = bgp_dest_get_prefix(dest);
if (!prefix_check
|| dest_p->prefixlen == match.prefixlen) {
@@ -12192,7 +12181,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
@@ -14759,7 +14748,8 @@ static int bgp_clear_damp_route(struct vty *vty, const char *view_name,
table = bgp_dest_get_bgp_table_info(dest);
if (!table)
continue;
- if ((rm = bgp_node_match(table, &match)) == NULL)
+ rm = bgp_node_match(table, &match);
+ if (rm == NULL)
continue;
const struct prefix *rm_p = bgp_dest_get_prefix(dest);
@@ -14771,9 +14761,8 @@ static int bgp_clear_damp_route(struct vty *vty, const char *view_name,
if (pi->extra && pi->extra->damp_info) {
pi_temp = pi->next;
bgp_damp_info_free(
- &pi->extra->damp_info,
- &bgp->damp[afi][safi],
- 1, afi, safi);
+ pi->extra->damp_info,
+ 1);
pi = pi_temp;
} else
pi = pi->next;
@@ -14783,8 +14772,8 @@ static int bgp_clear_damp_route(struct vty *vty, const char *view_name,
bgp_dest_unlock_node(rm);
}
} else {
- if ((dest = bgp_node_match(bgp->rib[afi][safi], &match))
- != NULL) {
+ dest = bgp_node_match(bgp->rib[afi][safi], &match);
+ if (dest != NULL) {
const struct prefix *dest_p = bgp_dest_get_prefix(dest);
if (!prefix_check
@@ -14809,9 +14798,8 @@ static int bgp_clear_damp_route(struct vty *vty, const char *view_name,
bdi->safi);
}
bgp_damp_info_free(
- &pi->extra->damp_info,
- &bgp->damp[afi][safi],
- 1, afi, safi);
+ pi->extra->damp_info,
+ 1);
pi = pi_temp;
} else
pi = pi->next;
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 4b71ba30ce..09dd71c020 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"
@@ -1180,6 +1181,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. */
@@ -4598,6 +4648,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,
@@ -6287,6 +6389,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);
@@ -6371,6 +6474,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 e541d117be..45f5c8f4bc 100644
--- a/bgpd/bgp_routemap_nb_config.c
+++ b/bgpd/bgp_routemap_nb_config.c
@@ -70,8 +70,8 @@ static int bgp_route_match_delete(struct route_map_index *index,
if (type != RMAP_EVENT_MATCH_DELETED) {
/* ignore the mundane, the types without any dependency */
if (arg == NULL) {
- if ((tmpstr = route_map_get_match_arg(index, command))
- != NULL)
+ tmpstr = route_map_get_match_arg(index, command);
+ if (tmpstr != NULL)
dep_name =
XSTRDUP(MTYPE_ROUTE_MAP_RULE, tmpstr);
} else {
@@ -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.c b/bgpd/bgp_updgrp.c
index b8545188a4..dd3309dad9 100644
--- a/bgpd/bgp_updgrp.c
+++ b/bgpd/bgp_updgrp.c
@@ -339,6 +339,7 @@ static unsigned int updgrp_hash_key_make(const void *p)
key);
key = jhash_1word(peer->v_routeadv, key);
key = jhash_1word(peer->change_local_as, key);
+ key = jhash_1word(peer->max_packet_size, key);
if (peer->group)
key = jhash_1word(jhash(peer->group->name,
@@ -572,6 +573,7 @@ static int update_group_show_walkcb(struct update_group *updgrp, void *arg)
struct update_subgroup *subgrp;
struct peer_af *paf;
struct bgp_filter *filter;
+ struct peer *peer = UPDGRP_PEER(updgrp);
int match = 0;
if (!ctx)
@@ -663,6 +665,9 @@ static int update_group_show_walkcb(struct update_group *updgrp, void *arg)
CHECK_FLAG(subgrp->flags, SUBGRP_FLAG_NEEDS_REFRESH)
? "R"
: "");
+ if (peer)
+ vty_out(vty, " Max packet size: %d\n",
+ peer->max_packet_size);
if (subgrp->peer_count > 0) {
vty_out(vty, " Peers:\n");
SUBGRP_FOREACH_PEER (subgrp, paf)
diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c
index 18829aa747..9c2288cba3 100644
--- a/bgpd/bgp_updgrp_adv.c
+++ b/bgpd/bgp_updgrp_adv.c
@@ -572,7 +572,8 @@ void bgp_adj_out_unset_subgroup(struct bgp_dest *dest,
return;
/* Lookup existing adjacency */
- if ((adj = adj_lookup(dest, subgrp, addpath_tx_id)) != NULL) {
+ adj = adj_lookup(dest, subgrp, addpath_tx_id);
+ if (adj != NULL) {
/* Clean up previous advertisement. */
if (adj->adv)
bgp_advertise_clean_subgroup(subgrp, adj);
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 e713894eb1..2b19f76c7b 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);
@@ -1857,9 +1858,8 @@ void cli_show_router_bgp_med_config(struct vty *vty, struct lyd_node *dnode,
uint32_t med_admin_val;
vty_out(vty, " bgp max-med administrative");
- if ((med_admin_val =
- yang_dnode_get_uint32(dnode, "./max-med-admin"))
- != BGP_MAXMED_VALUE_DEFAULT)
+ med_admin_val = yang_dnode_get_uint32(dnode, "./max-med-admin");
+ if (med_admin_val != BGP_MAXMED_VALUE_DEFAULT)
vty_out(vty, " %u", med_admin_val);
vty_out(vty, "\n");
}
@@ -8117,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",
@@ -10196,7 +10213,8 @@ static int bgp_clear_prefix(struct vty *vty, const char *view_name,
if (table == NULL)
continue;
- if ((rm = bgp_node_match(table, &match)) != NULL) {
+ rm = bgp_node_match(table, &match);
+ if (rm != NULL) {
const struct prefix *rm_p =
bgp_dest_get_prefix(rm);
@@ -10209,7 +10227,8 @@ static int bgp_clear_prefix(struct vty *vty, const char *view_name,
}
}
} else {
- if ((dest = bgp_node_match(rib, &match)) != NULL) {
+ dest = bgp_node_match(rib, &match);
+ if (dest != NULL) {
const struct prefix *dest_p = bgp_dest_get_prefix(dest);
if (dest_p->prefixlen == match.prefixlen) {
@@ -18145,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);
@@ -18264,7 +18290,7 @@ static struct cmd_node bgp_ipv4_labeled_unicast_node = {
};
static struct cmd_node bgp_ipv6_unicast_node = {
- .name = "bgp ipv6",
+ .name = "bgp ipv6 unicast",
.node = BGP_IPV6_NODE,
.parent_node = BGP_NODE,
.prompt = "%s(config-router-af)# ",
@@ -19433,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);
@@ -20943,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 ec71e17034..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);
@@ -2573,6 +2576,7 @@ static void bgp_encode_pbr_rule_action(struct stream *s,
stream_putl(s, pbr->unique);
else
stream_putl(s, pbra->unique);
+ stream_putc(s, 0); /* ip protocol being used */
if (pbr && pbr->flags & MATCH_IP_SRC_SET)
memcpy(&pfx, &(pbr->src), sizeof(struct prefix));
else {
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 197133cbb4..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++;
@@ -1369,7 +1364,7 @@ struct peer *peer_new(struct bgp *bgp)
peer->bgp = bgp_lock(bgp);
peer = peer_lock(peer); /* initial reference */
peer->password = NULL;
- peer->max_packet_size = BGP_MAX_PACKET_SIZE;
+ peer->max_packet_size = BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE;
/* Set default flags. */
FOREACH_AFI_SAFI (afi, safi) {
@@ -1466,6 +1461,8 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src)
(void)peer_sort(peer_dst);
peer_dst->rmap_type = peer_src->rmap_type;
+ peer_dst->max_packet_size = peer_src->max_packet_size;
+
/* Timers */
peer_dst->holdtime = peer_src->holdtime;
peer_dst->keepalive = peer_src->keepalive;
@@ -2315,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);
}
@@ -3097,8 +3087,7 @@ static struct bgp *bgp_create(as_t *as, const char *name,
afi_t afi;
safi_t safi;
- if ((bgp = XCALLOC(MTYPE_BGP, sizeof(struct bgp))) == NULL)
- return NULL;
+ bgp = XCALLOC(MTYPE_BGP, sizeof(struct bgp));
if (BGP_DEBUG(zebra, ZEBRA)) {
if (inst_type == BGP_INSTANCE_TYPE_DEFAULT)
@@ -3179,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 6108a37522..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],
@@ -672,8 +673,6 @@ AC_ARG_ENABLE([fpm],
AS_HELP_STRING([--enable-fpm], [enable Forwarding Plane Manager support]))
AC_ARG_ENABLE([pcep],
AS_HELP_STRING([--enable-pcep], [enable PCEP support for pathd]))
-AC_ARG_ENABLE([systemd],
- AS_HELP_STRING([--enable-systemd], [enable Systemd support]))
AC_ARG_ENABLE([werror],
AS_HELP_STRING([--enable-werror], [enable -Werror (recommended for developers only)]))
AC_ARG_ENABLE([cumulus],
@@ -705,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],
@@ -768,19 +767,6 @@ case "${enable_cpu_time}" in
;;
esac
-case "${enable_systemd}" in
- "no") ;;
- "yes")
- AC_CHECK_LIB([systemd], [sd_notify], [LIBS="$LIBS -lsystemd"])
- if test "$ac_cv_lib_systemd_sd_notify" = "no"; then
- AC_MSG_ERROR([enable systemd has been specified but systemd development env not found on your system])
- else
- AC_DEFINE([HAVE_SYSTEMD], [1], [Compile systemd support in])
- fi
- ;;
- "*") ;;
-esac
-
if test "$enable_rr_semantics" != "no" ; then
AC_DEFINE([HAVE_V6_RR_SEMANTICS], [1], [Compile in v6 Route Replacement Semantics])
fi
@@ -2529,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/debian/README.Debian b/debian/README.Debian
index a23a0efcac..53fb6c9d0e 100644
--- a/debian/README.Debian
+++ b/debian/README.Debian
@@ -31,10 +31,6 @@ The following Build Profiles have been added:
controls whether the RPKI module is built.
Will be enabled by default at some point, adds some extra dependencies.
-- pkg.frr.nosystemd
- Disables both systemd unit file installation as well as watchfrr sd_notify
- support at startup. Removes libsystemd dependency.
-
Note that all options have a "no" form; if you want to have your decision
be sticky regardless of changes to what it defaults to, then always use one
of the two. For example, all occurrences of <pkg.frr.rtrlib> will at some
@@ -43,10 +39,6 @@ point be replaced with <!pkg.frr.nortrlib>.
The main frr package has the exact same contents regardless of rtrlib or snmp
choices. The options only control frr-snmp and frr-rpki-rtrlib packages.
-The main frr package does NOT have the same contents if pkg.frr.nosystemd is
-used. This option should only be used for systems that do not have systemd,
-e.g. Ubuntu 14.04.
-
* Debian Policy compliance notes
================================
diff --git a/debian/control b/debian/control
index 0bbe99b312..0e67ff3730 100644
--- a/debian/control
+++ b/debian/control
@@ -22,7 +22,6 @@ Build-Depends: bison,
librtr-dev <!pkg.frr.nortrlib>,
libsnmp-dev,
libssh-dev <!pkg.frr.nortrlib>,
- libsystemd-dev <!pkg.frr.nosystemd>,
libyang2-dev,
lsb-base,
pkg-config,
diff --git a/debian/rules b/debian/rules
index 93d0cdb2a0..0fa9c3a3b0 100755
--- a/debian/rules
+++ b/debian/rules
@@ -21,14 +21,6 @@ else
CONF_RPKI=--disable-rpki
endif
-ifeq ($(filter pkg.frr.nosystemd,$(DEB_BUILD_PROFILES)),)
- DH_WITHOUT_SYSTEMD=
- CONF_SYSTEMD=--enable-systemd=yes
-else
- DH_WITHOUT_SYSTEMD=--without=systemd
- CONF_SYSTEMD=--enable-systemd=no
-endif
-
ifeq ($(filter pkg.frr.lua,$(DEB_BUILD_PROFILES)),)
CONF_LUA=--disable-scripting
else
@@ -38,7 +30,7 @@ endif
export PYTHON=python3
%:
- dh $@ $(DH_WITHOUT_SYSTEMD)
+ dh $@
override_dh_auto_configure:
$(shell dpkg-buildflags --export=sh); \
@@ -52,7 +44,6 @@ override_dh_auto_configure:
LIBTOOLFLAGS="-rpath /usr/lib/$(DEB_HOST_MULTIARCH)/frr" \
--disable-dependency-tracking \
\
- $(CONF_SYSTEMD) \
$(CONF_RPKI) \
$(CONF_LUA) \
--with-libpam \
@@ -80,9 +71,7 @@ override_dh_auto_install:
sed -e '1c #!/usr/bin/python3' -i debian/tmp/usr/lib/frr/generate_support_bundle.py
# let dh_systemd_* and dh_installinit do their thing automatically
-ifeq ($(filter pkg.frr.nosystemd,$(DEB_BUILD_PROFILES)),)
cp tools/frr.service debian/frr.service
-endif
cp tools/frrinit.sh debian/frr.init
-rm -f debian/tmp/usr/lib/frr/frr
diff --git a/doc/developer/building-frr-for-archlinux.rst b/doc/developer/building-frr-for-archlinux.rst
index e589a9f724..af1677e89e 100644
--- a/doc/developer/building-frr-for-archlinux.rst
+++ b/doc/developer/building-frr-for-archlinux.rst
@@ -10,8 +10,8 @@ Installing Dependencies
sudo pacman -S \
git autoconf automake libtool make cmake pcre readline texinfo \
pkg-config pam json-c bison flex python-pytest \
- c-ares python systemd python2-ipaddress python-sphinx \
- systemd-libs net-snmp perl libcap libelf
+ c-ares python python2-ipaddress python-sphinx \
+ net-snmp perl libcap libelf
.. include:: building-libyang.rst
diff --git a/doc/developer/building-frr-for-centos7.rst b/doc/developer/building-frr-for-centos7.rst
index 93b9993a38..ce11126f70 100644
--- a/doc/developer/building-frr-for-centos7.rst
+++ b/doc/developer/building-frr-for-centos7.rst
@@ -21,7 +21,7 @@ Add packages:
sudo yum install git autoconf automake libtool make \
readline-devel texinfo net-snmp-devel groff pkgconfig \
json-c-devel pam-devel bison flex pytest c-ares-devel \
- python-devel systemd-devel python-sphinx libcap-devel \
+ python-devel python-sphinx libcap-devel \
elfutils-libelf-devel
.. include:: building-libyang.rst
@@ -66,7 +66,6 @@ an example.)
--enable-user=frr \
--enable-group=frr \
--enable-vty-group=frrvty \
- --enable-systemd=yes \
--disable-ldpd \
--enable-fpm \
--with-pkg-git-version \
diff --git a/doc/developer/building-frr-for-centos8.rst b/doc/developer/building-frr-for-centos8.rst
index 65c93286b7..109a7866d9 100644
--- a/doc/developer/building-frr-for-centos8.rst
+++ b/doc/developer/building-frr-for-centos8.rst
@@ -14,7 +14,7 @@ Add packages:
sudo dnf install --enablerepo=PowerTools git autoconf pcre-devel \
automake libtool make readline-devel texinfo net-snmp-devel pkgconfig \
groff pkgconfig json-c-devel pam-devel bison flex python2-pytest \
- c-ares-devel python2-devel systemd-devel libcap-devel \
+ c-ares-devel python2-devel libcap-devel \
elfutils-libelf-devel
.. include:: building-libyang.rst
@@ -59,7 +59,6 @@ an example.)
--enable-user=frr \
--enable-group=frr \
--enable-vty-group=frrvty \
- --enable-systemd=yes \
--disable-ldpd \
--enable-fpm \
--with-pkg-git-version \
diff --git a/doc/developer/building-frr-for-debian8.rst b/doc/developer/building-frr-for-debian8.rst
index 475e0303fc..5e58854ed7 100644
--- a/doc/developer/building-frr-for-debian8.rst
+++ b/doc/developer/building-frr-for-debian8.rst
@@ -17,7 +17,7 @@ Add packages:
sudo apt-get install git autoconf automake libtool make \
libreadline-dev texinfo libjson-c-dev pkg-config bison flex python3-pip \
- libc-ares-dev python3-dev python3-sphinx build-essential libsystemd-dev \
+ libc-ares-dev python3-dev python3-sphinx build-essential \
libsnmp-dev libcap-dev libelf-dev
Install newer pytest (>3.0) from pip
diff --git a/doc/developer/building-frr-for-debian9.rst b/doc/developer/building-frr-for-debian9.rst
index 1981127b3d..f8d8025f62 100644
--- a/doc/developer/building-frr-for-debian9.rst
+++ b/doc/developer/building-frr-for-debian9.rst
@@ -11,7 +11,7 @@ Add packages:
sudo apt-get install git autoconf automake libtool make \
libreadline-dev texinfo libjson-c-dev pkg-config bison flex \
libc-ares-dev python3-dev python3-pytest python3-sphinx build-essential \
- libsnmp-dev libsystemd-dev libcap-dev libelf-dev
+ libsnmp-dev libcap-dev libelf-dev
.. include:: building-libyang.rst
diff --git a/doc/developer/building-frr-for-fedora.rst b/doc/developer/building-frr-for-fedora.rst
index 5fecd8a826..6ce76ba158 100644
--- a/doc/developer/building-frr-for-fedora.rst
+++ b/doc/developer/building-frr-for-fedora.rst
@@ -14,7 +14,7 @@ Installing Dependencies
sudo dnf install git autoconf automake libtool make \
readline-devel texinfo net-snmp-devel groff pkgconfig json-c-devel \
pam-devel python3-pytest bison flex c-ares-devel python3-devel \
- python3-sphinx perl-core patch systemd-devel libcap-devel \
+ python3-sphinx perl-core patch libcap-devel \
elfutils-libelf-devel
.. include:: building-libyang.rst
diff --git a/doc/developer/building-frr-for-opensuse.rst b/doc/developer/building-frr-for-opensuse.rst
index 4e886e9c25..ee6a36a14b 100644
--- a/doc/developer/building-frr-for-opensuse.rst
+++ b/doc/developer/building-frr-for-opensuse.rst
@@ -13,7 +13,7 @@ Installing Dependencies
zypper in git autoconf automake libtool make \
readline-devel texinfo net-snmp-devel groff pkgconfig libjson-c-devel\
pam-devel python3-pytest bison flex c-ares-devel python3-devel\
- python3-Sphinx perl patch systemd-devel libcap-devel libyang-devel \
+ python3-Sphinx perl patch libcap-devel libyang-devel \
libelf-devel
Building & Installing FRR
diff --git a/doc/developer/building-frr-for-ubuntu1604.rst b/doc/developer/building-frr-for-ubuntu1604.rst
index 2cb9536f9b..d79545c859 100644
--- a/doc/developer/building-frr-for-ubuntu1604.rst
+++ b/doc/developer/building-frr-for-ubuntu1604.rst
@@ -13,8 +13,8 @@ Installing Dependencies
apt-get install \
git autoconf automake libtool make libreadline-dev texinfo \
pkg-config libpam0g-dev libjson-c-dev bison flex python3-pytest \
- libc-ares-dev python3-dev libsystemd-dev python-ipaddress python3-sphinx \
- install-info build-essential libsystemd-dev libsnmp-dev perl libcap-dev \
+ libc-ares-dev python3-dev python-ipaddress python3-sphinx \
+ install-info build-essential libsnmp-dev perl libcap-dev \
libelf-dev
.. include:: building-libyang.rst
diff --git a/doc/developer/building-frr-for-ubuntu1804.rst b/doc/developer/building-frr-for-ubuntu1804.rst
index eb3991c139..39a17fc01c 100644
--- a/doc/developer/building-frr-for-ubuntu1804.rst
+++ b/doc/developer/building-frr-for-ubuntu1804.rst
@@ -13,8 +13,8 @@ Installing Dependencies
sudo apt-get install \
git autoconf automake libtool make libreadline-dev texinfo \
pkg-config libpam0g-dev libjson-c-dev bison flex python3-pytest \
- libc-ares-dev python3-dev libsystemd-dev python-ipaddress python3-sphinx \
- install-info build-essential libsystemd-dev libsnmp-dev perl libcap-dev \
+ libc-ares-dev python3-dev python-ipaddress python3-sphinx \
+ install-info build-essential libsnmp-dev perl libcap-dev \
libelf-dev
.. include:: building-libyang.rst
diff --git a/doc/developer/building-frr-for-ubuntu2004.rst b/doc/developer/building-frr-for-ubuntu2004.rst
index 58d72e2891..92ddead4a5 100644
--- a/doc/developer/building-frr-for-ubuntu2004.rst
+++ b/doc/developer/building-frr-for-ubuntu2004.rst
@@ -13,8 +13,8 @@ Installing Dependencies
sudo apt-get install \
git autoconf automake libtool make libreadline-dev texinfo \
pkg-config libpam0g-dev libjson-c-dev bison flex python3-pytest \
- libc-ares-dev python3-dev libsystemd-dev python-ipaddress python3-sphinx \
- install-info build-essential libsystemd-dev libsnmp-dev perl \
+ libc-ares-dev python3-dev python-ipaddress python3-sphinx \
+ install-info build-essential libsnmp-dev perl \
libcap-dev python2 libelf-dev
Note that Ubuntu 20 no longer installs python 2.x, so it must be
diff --git a/doc/developer/include-compile.rst b/doc/developer/include-compile.rst
index d9fa260221..513cac6179 100644
--- a/doc/developer/include-compile.rst
+++ b/doc/developer/include-compile.rst
@@ -2,11 +2,6 @@ Clone the FRR git repo and use the included ``configure`` script to configure
FRR's build time options to your liking. The full option listing can be
obtained by running ``./configure -h``. The options shown below are examples.
-.. note::
-
- If your platform uses ``systemd``, please make sure to add
- ``--enable-systemd=yes`` to your configure options.
-
.. code-block:: console
git clone https://github.com/frrouting/frr.git frr
diff --git a/doc/developer/packaging-debian.rst b/doc/developer/packaging-debian.rst
index 968e960267..a81e052490 100644
--- a/doc/developer/packaging-debian.rst
+++ b/doc/developer/packaging-debian.rst
@@ -53,7 +53,7 @@ buster.)
Where `$options` may contain any or all of the following items:
* build profiles specified with ``-P``, e.g.
- ``-Ppkg.frr.nortrlib,pkg.frr.nosystemd``.
+ ``-Ppkg.frr.nortrlib,pkg.frr.rtrlib``.
Multiple values are separated by commas and there must not be a space
after the ``-P``.
@@ -64,14 +64,6 @@ buster.)
+================+===================+=========================================+
| pkg.frr.rtrlib | pkg.frr.nortrlib | builds frr-rpki-rtrlib package (or not) |
+----------------+-------------------+-----------------------------------------+
- | n/a | pkg.frr.nosystemd | removes libsystemd dependency and |
- | | | disables unit file installation |
- +----------------+-------------------+-----------------------------------------+
-
- .. note::
-
- The ``pkg.frr.nosystemd`` option is only intended to support Ubuntu
- 14.04 (and should be enabled when building for that.)
* the ``-uc -us`` options to disable signing the packages with your GPG key
diff --git a/doc/developer/packaging-redhat.rst b/doc/developer/packaging-redhat.rst
index 458cfa0ad4..9e64b912f3 100644
--- a/doc/developer/packaging-redhat.rst
+++ b/doc/developer/packaging-redhat.rst
@@ -18,10 +18,6 @@ Tested on CentOS 6, CentOS 7, CentOS 8 and Fedora 24.
yum install rpm-build net-snmp-devel pam-devel libcap-devel
- If your platform uses systemd::
-
- yum install systemd-devel
-
For CentOS 7 and CentOS 8, the package will be built using python3
and requires additional python3 packages::
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..32a51fdb70 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
@@ -3508,17 +3539,17 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`.
It helps to identify which prefixes were installed at some point.
Here is an example of how to check what prefixes were installed starting
- with an arbitrary version::
+ with an arbitrary version:
- .. code-block:: frr
+.. code-block:: shell
- ~# vtysh -c 'show bgp ipv4 unicast json' | jq '.tableVersion'
- 9
- ~# vtysh -c 'show ip bgp version 9 json' | jq -r '.routes | keys[]'
- 192.168.3.0/24
- ~# vtysh -c 'show ip bgp version 8 json' | jq -r '.routes | keys[]'
- 192.168.2.0/24
- 192.168.3.0/24
+ # vtysh -c 'show bgp ipv4 unicast json' | jq '.tableVersion'
+ 9
+ # vtysh -c 'show ip bgp version 9 json' | jq -r '.routes | keys[]'
+ 192.168.3.0/24
+ # vtysh -c 'show ip bgp version 8 json' | jq -r '.routes | keys[]'
+ 192.168.2.0/24
+ 192.168.3.0/24
.. clicmd:: show bgp [afi] [safi] statistics
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/installation.rst b/doc/user/installation.rst
index 63254555d9..fdf5bab9c9 100644
--- a/doc/user/installation.rst
+++ b/doc/user/installation.rst
@@ -146,11 +146,6 @@ options from the list below.
software available on your machine. This is needed for systemd integration, if you
disable watchfrr you cannot have any systemd integration.
-.. option:: --enable-systemd
-
- Build watchfrr with systemd integration, this will allow FRR to communicate with
- systemd to tell systemd if FRR has come up properly.
-
.. option:: --enable-werror
Build with all warnings converted to errors as a compile option. This
@@ -404,6 +399,12 @@ options to the configuration script.
Set StrongSWAN vici interface socket path [/var/run/charon.vici].
+.. note::
+
+ The former ``--enable-systemd`` option does not exist anymore. Support for
+ systemd is now always available through built-in functions, without
+ depending on libsystemd.
+
Python dependency, documentation and tests
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst
index 04ee1643d8..f8595ef3f5 100644
--- a/doc/user/ospf6d.rst
+++ b/doc/user/ospf6d.rst
@@ -85,6 +85,138 @@ OSPF6 router
change to take effect, user can use this cli instead of restarting the
ospf6d daemon.
+ASBR Summarisation Support in OSPFv3
+====================================
+
+ External routes in OSPFv3 are carried by type 5/7 LSA (external LSAs).
+ External LSAs are generated by ASBR (Autonomous System Boundary Router).
+ Large topology database requires a large amount of router memory, which
+ slows down all processes, including SPF calculations.
+ It is necessary to reduce the size of the OSPFv3 topology database,
+ especially in a large network. Summarising routes keeps the routing
+ tables smaller and easier to troubleshoot.
+
+ External route summarization must be configured on ASBR.
+ Stub area do not allow ASBR because they don’t allow type 5 LSAs.
+
+ An ASBR will inject a summary route into the OSPFv3 domain.
+
+ Summary route will only be advertised if you have at least one subnet
+ that falls within the summary range.
+
+ Users will be allowed an option in the CLI to not advertise range of
+ ipv6 prefixes as well.
+
+ The configuration of ASBR Summarisation is supported using the CLI command
+
+.. clicmd:: summary-address X:X::X:X/M [tag (1-4294967295)] [{metric (0-16777215) | metric-type (1-2)}]
+
+ This command will advertise a single External LSA on behalf of all the
+ prefixes falling under this range configured by the CLI.
+ The user is allowed to configure tag, metric and metric-type as well.
+ By default, tag is not configured, default metric as 20 and metric-type
+ as type-2 gets advertised.
+ A summary route is created when one or more specific routes are learned and
+ removed when no more specific route exist.
+ The summary route is also installed in the local system with Null0 as
+ next-hop to avoid leaking traffic.
+
+.. clicmd:: no summary-address X:X::X:X/M [tag (1-4294967295)] [{metric (0-16777215) | metric-type (1-2)}]
+
+ This command can be used to remove the summarisation configuration.
+ This will flush the single External LSA if it was originated and advertise
+ the External LSAs for all the existing individual prefixes.
+
+.. clicmd:: summary-address X:X::X:X/M no-advertise
+
+ This command can be used when user do not want to advertise a certain
+ range of prefixes using the no-advertise option.
+ This command when configured will flush all the existing external LSAs
+ falling under this range.
+
+.. clicmd:: no summary-address X:X::X:X/M no-advertise
+
+ This command can be used to remove the previous configuration.
+ When configured, tt will resume originating external LSAs for all the prefixes
+ falling under the configured range.
+
+.. clicmd:: aggregation timer (5-1800)
+
+ The summarisation command takes effect after the aggregation timer expires.
+ By default the value of this timer is 5 seconds. User can modify the time
+ after which the external LSAs should get originated using this command.
+
+.. clicmd:: no aggregation timer (5-1800)
+
+ This command removes the timer configuration. It reverts back to default
+ 5 second timer.
+
+.. clicmd:: show ipv6 ospf6 summary-address [detail] [json]
+
+ This command can be used to see all the summary-address related information.
+ When detail option is used, it shows all the prefixes falling under each
+ summary-configuration apart from other information.
+
+.. clicmd:: debug ospf6 lsa aggregation
+
+ This command can be used to enable the debugs related to the summarisation
+ of these LSAs.
+
+.. _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 +464,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/pbr.rst b/doc/user/pbr.rst
index 77134a7704..e59ed10896 100644
--- a/doc/user/pbr.rst
+++ b/doc/user/pbr.rst
@@ -117,6 +117,21 @@ end destination.
both v4 and v6 prefixes. This command is used in conjunction of the
:clicmd:`match src-ip PREFIX` command for matching.
+.. clicmd:: match src-port (1-65535)
+
+ When a incoming packet matches the source port specified, take the
+ packet and forward according to the nexthops specified.
+
+.. clicmd:: match dst-port (1-65535)
+
+ When a incoming packet matches the destination port specified, take the
+ packet and forward according to the nexthops specified.
+
+.. clicmd:: match ip-protocol [tcp|udp]
+
+ When a incoming packet matches the specified ip protocol, take the
+ packet and forward according to the nexthops specified.
+
.. clicmd:: match mark (1-4294967295)
Select the mark to match. This is a linux only command and if attempted
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/docker/centos-7/Dockerfile b/docker/centos-7/Dockerfile
index 303a33fe4a..73097df8fa 100644
--- a/docker/centos-7/Dockerfile
+++ b/docker/centos-7/Dockerfile
@@ -4,7 +4,7 @@ RUN yum install -y epel-release
RUN yum install -y rpm-build autoconf automake libtool make \
readline-devel texinfo net-snmp-devel groff pkgconfig \
json-c-devel pam-devel bison flex pytest c-ares-devel \
- python3-devel python3-sphinx systemd-devel libcap-devel \
+ python3-devel python3-sphinx libcap-devel \
https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-7-x86_64-Packages/libyang2-2.0.0.10.g2eb910e4-1.el7.x86_64.rpm \
https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-7-x86_64-Packages/libyang2-devel-2.0.0.10.g2eb910e4-1.el7.x86_64.rpm \
https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm \
diff --git a/docker/centos-8/Dockerfile b/docker/centos-8/Dockerfile
index 8a0c28e13b..71378c2451 100644
--- a/docker/centos-8/Dockerfile
+++ b/docker/centos-8/Dockerfile
@@ -4,7 +4,7 @@ FROM centos:centos8 as centos-8-builder
RUN dnf install --enablerepo=powertools -y rpm-build git autoconf pcre-devel \
automake libtool make readline-devel texinfo net-snmp-devel pkgconfig \
groff pkgconfig json-c-devel pam-devel bison flex python3-pytest \
- c-ares-devel python3-devel python3-sphinx systemd-devel libcap-devel platform-python-devel \
+ c-ares-devel python3-devel python3-sphinx libcap-devel platform-python-devel \
https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \
https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-devel-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \
https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm \
diff --git a/docker/ubuntu18-ci/Dockerfile b/docker/ubuntu18-ci/Dockerfile
index 680b9172ad..766f06dfc2 100644
--- a/docker/ubuntu18-ci/Dockerfile
+++ b/docker/ubuntu18-ci/Dockerfile
@@ -7,8 +7,8 @@ RUN apt update && \
apt-get install -y \
git autoconf automake libtool make libreadline-dev texinfo \
pkg-config libpam0g-dev libjson-c-dev bison flex python3-pytest \
- libc-ares-dev python3-dev libsystemd-dev python-ipaddress python3-sphinx \
- install-info build-essential libsystemd-dev libsnmp-dev perl libcap-dev \
+ libc-ares-dev python3-dev python-ipaddress python3-sphinx \
+ install-info build-essential libsnmp-dev perl libcap-dev \
libelf-dev \
sudo gdb iputils-ping time \
mininet python-pip iproute2 iperf && \
diff --git a/docker/ubuntu20-ci/Dockerfile b/docker/ubuntu20-ci/Dockerfile
index 47d5b81d3a..b5df98f23e 100644
--- a/docker/ubuntu20-ci/Dockerfile
+++ b/docker/ubuntu20-ci/Dockerfile
@@ -7,8 +7,8 @@ RUN apt update && \
apt-get install -y \
git autoconf automake libtool make libreadline-dev texinfo \
pkg-config libpam0g-dev libjson-c-dev bison flex python3-pytest \
- libc-ares-dev python3-dev libsystemd-dev python-ipaddress python3-sphinx \
- install-info build-essential libsystemd-dev libsnmp-dev perl \
+ libc-ares-dev python3-dev python-ipaddress python3-sphinx \
+ install-info build-essential libsnmp-dev perl \
libcap-dev python2 libelf-dev \
sudo gdb curl iputils-ping time \
libgrpc++-dev libgrpc-dev protobuf-compiler-grpc \
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/buffer.c b/lib/buffer.c
index 7929b3709d..41b1adc9fc 100644
--- a/lib/buffer.c
+++ b/lib/buffer.c
@@ -357,7 +357,8 @@ buffer_status_t buffer_flush_window(struct buffer *b, int fd, int width,
iov_size =
((iov_index > IOV_MAX) ? IOV_MAX : iov_index);
- if ((nbytes = writev(fd, c_iov, iov_size)) < 0) {
+ nbytes = writev(fd, c_iov, iov_size);
+ if (nbytes < 0) {
flog_err(EC_LIB_SOCKET,
"%s: writev to fd %d failed: %s",
__func__, fd, safe_strerror(errno));
@@ -370,7 +371,8 @@ buffer_status_t buffer_flush_window(struct buffer *b, int fd, int width,
}
}
#else /* IOV_MAX */
- if ((nbytes = writev(fd, iov, iov_index)) < 0)
+ nbytes = writev(fd, iov, iov_index);
+ if (nbytes < 0)
flog_err(EC_LIB_SOCKET, "%s: writev to fd %d failed: %s",
__func__, fd, safe_strerror(errno));
#endif /* IOV_MAX */
@@ -472,13 +474,17 @@ buffer_status_t buffer_write(struct buffer *b, int fd, const void *p,
/* Buffer is not empty, so do not attempt to write the new data.
*/
nbytes = 0;
- else if ((nbytes = write(fd, p, size)) < 0) {
- if (ERRNO_IO_RETRY(errno))
- nbytes = 0;
- else {
- flog_err(EC_LIB_SOCKET, "%s: write error on fd %d: %s",
- __func__, fd, safe_strerror(errno));
- return BUFFER_ERROR;
+ else {
+ nbytes = write(fd, p, size);
+ if (nbytes < 0) {
+ if (ERRNO_IO_RETRY(errno))
+ nbytes = 0;
+ else {
+ flog_err(EC_LIB_SOCKET,
+ "%s: write error on fd %d: %s",
+ __func__, fd, safe_strerror(errno));
+ return BUFFER_ERROR;
+ }
}
}
/* Add any remaining data to the buffer. */
diff --git a/lib/command.c b/lib/command.c
index 122e189342..422544b70b 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/csv.c b/lib/csv.c
index b48b79792e..05b9dbe39e 100644
--- a/lib/csv.c
+++ b/lib/csv.c
@@ -641,7 +641,8 @@ static int get_memory_usage(pid_t pid)
char *vm;
snprintf(status_child, sizeof(status_child), "/proc/%d/status", pid);
- if ((fd = open(status_child, O_RDONLY)) < 0)
+ fd = open(status_child, O_RDONLY);
+ if (fd < 0)
return -1;
read(fd, buf, 4095);
diff --git a/lib/frr_zmq.c b/lib/frr_zmq.c
index ce52848a25..ea9c828f7c 100644
--- a/lib/frr_zmq.c
+++ b/lib/frr_zmq.c
@@ -17,6 +17,14 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+/*
+ * IF YOU MODIFY THIS FILE PLEASE RUN `make check` and ensure that
+ * the test_zmq.c unit test is still working. There are dependancies
+ * between the two that are extremely fragile. My understanding
+ * is that there is specialized ownership of the cb pointer based
+ * upon what is happening. Those assumptions are supposed to be
+ * tested in the test_zmq.c
+ */
#include <zebra.h>
#include <zmq.h>
@@ -309,8 +317,22 @@ void frrzmq_thread_cancel(struct frrzmq_cb **cb, struct cb_core *core)
core->cancelled = true;
thread_cancel(&core->thread);
+ /*
+ * Looking at this code one would assume that FRR
+ * would want a `!(*cb)->write.thread. This was
+ * attempted in e08165def1c62beee0e87385 but this
+ * change caused `make check` to stop working
+ * which was not noticed because our CI system
+ * does not build with zeromq. Put this back
+ * to the code as written in 2017. e08165de..
+ * was introduced in 2021. So someone was ok
+ * with frrzmq_thread_cancel for 4 years. This will
+ * allow those people doing `make check` to continue
+ * working. In the meantime if the people using
+ * this code see an issue they can fix it
+ */
if ((*cb)->read.cancelled && !(*cb)->read.thread
- && (*cb)->write.cancelled && !(*cb)->write.thread)
+ && (*cb)->write.cancelled && (*cb)->write.thread)
XFREE(MTYPE_ZEROMQ_CB, *cb);
}
diff --git a/lib/libfrr.c b/lib/libfrr.c
index 0817182f7a..97dab74d9b 100644
--- a/lib/libfrr.c
+++ b/lib/libfrr.c
@@ -44,6 +44,7 @@
#include "frr_pthread.h"
#include "defaults.h"
#include "frrscript.h"
+#include "systemd.h"
DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm));
DEFINE_HOOK(frr_config_pre, (struct thread_master * tm), (tm));
@@ -363,6 +364,11 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv)
startup_fds |= UINT64_C(0x1) << (uint64_t)i;
}
+
+ /* note this doesn't do anything, it just grabs state, so doing it
+ * early in _preinit is perfect.
+ */
+ systemd_init_env();
}
bool frr_is_startup_fd(int fd)
diff --git a/lib/link_state.c b/lib/link_state.c
index e8a6b89f89..062384aac7 100644
--- a/lib/link_state.c
+++ b/lib/link_state.c
@@ -538,8 +538,6 @@ struct ls_edge *ls_edge_add(struct ls_ted *ted,
/* Create Edge and add it to the TED */
new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_edge));
- if (!new)
- return NULL;
new->attributes = attributes;
new->key = key;
@@ -804,8 +802,6 @@ struct ls_ted *ls_ted_new(const uint32_t key, const char *name,
struct ls_ted *new;
new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_ted));
- if (new == NULL)
- return new;
/* Set basic information for this ted */
new->key = key;
@@ -1005,8 +1001,6 @@ static struct ls_node *ls_parse_node(struct stream *s)
size_t len;
node = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_node));
- if (node == NULL)
- return NULL;
STREAM_GET(&node->adv, s, sizeof(struct ls_node_id));
STREAM_GETW(s, node->flags);
@@ -1051,8 +1045,6 @@ static struct ls_attributes *ls_parse_attributes(struct stream *s)
size_t len;
attr = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_attributes));
- if (attr == NULL)
- return NULL;
attr->srlgs = NULL;
STREAM_GET(&attr->adv, s, sizeof(struct ls_node_id));
@@ -1157,8 +1149,6 @@ static struct ls_prefix *ls_parse_prefix(struct stream *s)
size_t len;
ls_pref = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_prefix));
- if (ls_pref == NULL)
- return NULL;
STREAM_GET(&ls_pref->adv, s, sizeof(struct ls_node_id));
STREAM_GETW(s, ls_pref->flags);
@@ -1193,8 +1183,6 @@ struct ls_message *ls_parse_msg(struct stream *s)
struct ls_message *msg;
msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message));
- if (msg == NULL)
- return NULL;
/* Read LS Message header */
STREAM_GETC(s, msg->event);
diff --git a/lib/network.c b/lib/network.c
index 411661a5e1..b60ad9a57c 100644
--- a/lib/network.c
+++ b/lib/network.c
@@ -78,7 +78,8 @@ int set_nonblocking(int fd)
/* According to the Single UNIX Spec, the return value for F_GETFL
should
never be negative. */
- if ((flags = fcntl(fd, F_GETFL)) < 0) {
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0) {
flog_err(EC_LIB_SYSTEM_CALL,
"fcntl(F_GETFL) failed for fd %d: %s", fd,
safe_strerror(errno));
diff --git a/lib/ntop.c b/lib/ntop.c
index ccbf8793d3..1b2dd7a6d1 100644
--- a/lib/ntop.c
+++ b/lib/ntop.c
@@ -40,14 +40,18 @@ static inline void putbyte(uint8_t bytex, char **posx)
bool zero = false;
int byte = bytex, tmp, a, b;
- if ((tmp = byte - 200) >= 0) {
+ tmp = byte - 200;
+ if (tmp >= 0) {
*pos++ = '2';
zero = true;
byte = tmp;
- } else if ((tmp = byte - 100) >= 0) {
- *pos++ = '1';
- zero = true;
- byte = tmp;
+ } else {
+ tmp = byte - 100;
+ if (tmp >= 0) {
+ *pos++ = '1';
+ zero = true;
+ byte = tmp;
+ }
}
/* make sure the compiler knows the value range of "byte" */
diff --git a/lib/pbr.h b/lib/pbr.h
index e365888662..cef1d9d380 100644
--- a/lib/pbr.h
+++ b/lib/pbr.h
@@ -49,7 +49,8 @@ struct pbr_filter {
#define PBR_FILTER_PROTO (1 << 5)
#define PBR_FILTER_SRC_PORT_RANGE (1 << 6)
#define PBR_FILTER_DST_PORT_RANGE (1 << 7)
-#define PBR_FILTER_DSFIELD (1 << 8)
+#define PBR_FILTER_DSFIELD (1 << 8)
+#define PBR_FILTER_IP_PROTOCOL (1 << 9)
#define PBR_DSFIELD_DSCP (0xfc) /* Upper 6 bits of DS field: DSCP */
#define PBR_DSFIELD_ECN (0x03) /* Lower 2 bits of DS field: BCN */
@@ -67,6 +68,9 @@ struct pbr_filter {
/* Filter with fwmark */
uint32_t fwmark;
+
+ /* Filter with the ip protocol */
+ uint8_t ip_proto;
};
/*
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.c b/lib/prefix.c
index 1c57715e8f..ef7d2e59da 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -1034,7 +1034,8 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size)
l = strlen(buf);
buf[l++] = '/';
byte = p->prefixlen;
- if ((tmp = p->prefixlen - 100) >= 0) {
+ tmp = p->prefixlen - 100;
+ if (tmp >= 0) {
buf[l++] = '1';
z = true;
byte = tmp;
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/sockopt.c b/lib/sockopt.c
index 98bfda5079..150736e00c 100644
--- a/lib/sockopt.c
+++ b/lib/sockopt.c
@@ -379,14 +379,14 @@ static int setsockopt_ipv4_ifindex(int sock, ifindex_t val)
int ret;
#if defined(IP_PKTINFO)
- if ((ret = setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof(val)))
- < 0)
+ ret = setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof(val));
+ if (ret < 0)
flog_err(EC_LIB_SOCKET,
"Can't set IP_PKTINFO option for fd %d to %d: %s",
sock, val, safe_strerror(errno));
#elif defined(IP_RECVIF)
- if ((ret = setsockopt(sock, IPPROTO_IP, IP_RECVIF, &val, sizeof(val)))
- < 0)
+ ret = setsockopt(sock, IPPROTO_IP, IP_RECVIF, &val, sizeof(val));
+ if (ret < 0)
flog_err(EC_LIB_SOCKET,
"Can't set IP_RECVIF option for fd %d to %d: %s", sock,
val, safe_strerror(errno));
@@ -639,12 +639,8 @@ int sockopt_tcp_signature_ext(int sock, union sockunion *su, uint16_t prefixlen,
#endif /* GNU_LINUX */
- if ((ret = setsockopt(sock, IPPROTO_TCP, optname, &md5sig,
- sizeof(md5sig)))
- < 0) {
- /* ENOENT is harmless. It is returned when we clear a password
- for which
- one was not previously set. */
+ ret = setsockopt(sock, IPPROTO_TCP, optname, &md5sig, sizeof(md5sig));
+ if (ret < 0) {
if (ENOENT == errno)
ret = 0;
else
diff --git a/lib/stream.c b/lib/stream.c
index 904ee73b10..1557500c60 100644
--- a/lib/stream.c
+++ b/lib/stream.c
@@ -1097,7 +1097,8 @@ ssize_t stream_read_try(struct stream *s, int fd, size_t size)
return -1;
}
- if ((nbytes = read(fd, s->data + s->endp, size)) >= 0) {
+ nbytes = read(fd, s->data + s->endp, size);
+ if (nbytes >= 0) {
s->endp += nbytes;
return nbytes;
}
@@ -1126,9 +1127,8 @@ ssize_t stream_recvfrom(struct stream *s, int fd, size_t size, int flags,
return -1;
}
- if ((nbytes = recvfrom(fd, s->data + s->endp, size, flags, from,
- fromlen))
- >= 0) {
+ nbytes = recvfrom(fd, s->data + s->endp, size, flags, from, fromlen);
+ if (nbytes >= 0) {
s->endp += nbytes;
return nbytes;
}
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/lib/systemd.c b/lib/systemd.c
index c5cc3aa447..2238dc9f3d 100644
--- a/lib/systemd.c
+++ b/lib/systemd.c
@@ -20,68 +20,56 @@
*/
#include <zebra.h>
+#include <sys/un.h>
#include "thread.h"
#include "systemd.h"
+#include "lib_errors.h"
-#if defined HAVE_SYSTEMD
-#include <systemd/sd-daemon.h>
-#endif
+/* these are cleared from env so they don't "leak" into things we fork(),
+ * particularly for watchfrr starting individual daemons
+ *
+ * watchdog_pid is currently not used since watchfrr starts forking.
+ * (TODO: handle that better, somehow?)
+ */
+static pid_t watchdog_pid = -1;
+static intmax_t watchdog_msec;
+
+/* not used yet, but can trigger auto-switch to journald logging */
+bool sd_stdout_is_journal;
+bool sd_stderr_is_journal;
+
+static char *notify_socket;
-/*
- * Wrapper this silliness if we
- * don't have systemd
+/* talk to whatever entity claims to be systemd ;)
+ *
+ * refer to sd_notify docs for messages systemd accepts over this socket.
+ * This function should be functionally equivalent to sd_notify().
*/
static void systemd_send_information(const char *info)
{
-#if defined HAVE_SYSTEMD
- sd_notify(0, info);
-#else
- return;
-#endif
-}
+ int sock;
+ struct sockaddr_un sun;
-/*
- * A return of 0 means that we are not watchdoged
- */
-static int systemd_get_watchdog_time(int the_process)
-{
-#if defined HAVE_SYSTEMD
- uint64_t usec;
- char *watchdog = NULL;
- int ret;
-
- ret = sd_watchdog_enabled(0, &usec);
-
- /*
- * If return is 0 -> we don't want watchdog
- * if return is < 0, some sort of failure occurred
- */
- if (ret < 0)
- return 0;
-
- /*
- * systemd can return that this process
- * is not the expected sender of the watchdog timer
- * If we set the_process = 0 then we expect to
- * be able to send the watchdog to systemd
- * irrelevant of the pid of this process.
- */
- if (ret == 0 && the_process)
- return 0;
-
- if (ret == 0 && !the_process) {
- watchdog = getenv("WATCHDOG_USEC");
- if (!watchdog)
- return 0;
-
- usec = atol(watchdog);
- }
+ if (!notify_socket)
+ return;
+
+ sock = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (sock < 0)
+ return;
+
+ sun.sun_family = AF_UNIX;
+ strlcpy(sun.sun_path, notify_socket, sizeof(sun.sun_path));
- return (usec / 1000000) / 3;
-#else
- return 0;
-#endif
+ /* linux abstract unix socket namespace */
+ if (sun.sun_path[0] == '@')
+ sun.sun_path[0] = '\0';
+
+ /* nothing we can do if this errors out... */
+ (void)sendto(sock, info, strlen(info), 0, (struct sockaddr *)&sun,
+ sizeof(sun));
+
+ close(sock);
}
void systemd_send_stopping(void)
@@ -90,34 +78,27 @@ void systemd_send_stopping(void)
systemd_send_information("STOPPING=1");
}
-/*
- * How many seconds should we wait between watchdog sends
- */
-static int wsecs = 0;
static struct thread_master *systemd_master = NULL;
static int systemd_send_watchdog(struct thread *t)
{
systemd_send_information("WATCHDOG=1");
- thread_add_timer(systemd_master, systemd_send_watchdog, NULL, wsecs,
- NULL);
-
+ assert(watchdog_msec > 0);
+ thread_add_timer_msec(systemd_master, systemd_send_watchdog, NULL,
+ watchdog_msec, NULL);
return 1;
}
-void systemd_send_started(struct thread_master *m, int the_process)
+void systemd_send_started(struct thread_master *m)
{
assert(m != NULL);
- wsecs = systemd_get_watchdog_time(the_process);
systemd_master = m;
systemd_send_information("READY=1");
- if (wsecs != 0) {
- systemd_send_information("WATCHDOG=1");
- thread_add_timer(m, systemd_send_watchdog, m, wsecs, NULL);
- }
+ if (watchdog_msec > 0)
+ systemd_send_watchdog(NULL);
}
void systemd_send_status(const char *status)
@@ -127,3 +108,65 @@ void systemd_send_status(const char *status)
snprintf(buffer, sizeof(buffer), "STATUS=%s", status);
systemd_send_information(buffer);
}
+
+static intmax_t getenv_int(const char *varname, intmax_t dflt)
+{
+ char *val, *err;
+ intmax_t intval;
+
+ val = getenv(varname);
+ if (!val)
+ return dflt;
+
+ intval = strtoimax(val, &err, 0);
+ if (*err || !*val)
+ return dflt;
+ return intval;
+}
+
+void systemd_init_env(void)
+{
+ char *tmp;
+ uintmax_t dev, ino;
+ int len;
+ struct stat st;
+
+ notify_socket = getenv("NOTIFY_SOCKET");
+
+ /* no point in setting up watchdog w/o notify socket */
+ if (notify_socket) {
+ intmax_t watchdog_usec;
+
+ watchdog_pid = getenv_int("WATCHDOG_PID", -1);
+ if (watchdog_pid <= 0)
+ watchdog_pid = -1;
+
+ /* note this is the deadline, hence the divide by 3 */
+ watchdog_usec = getenv_int("WATCHDOG_USEC", 0);
+ if (watchdog_usec >= 3000)
+ watchdog_msec = watchdog_usec / 3000;
+ else {
+ if (watchdog_usec != 0)
+ flog_err(
+ EC_LIB_UNAVAILABLE,
+ "systemd expects a %jd microsecond watchdog timer, but FRR only supports millisecond resolution!",
+ watchdog_usec);
+ watchdog_msec = 0;
+ }
+ }
+
+ tmp = getenv("JOURNAL_STREAM");
+ if (tmp && sscanf(tmp, "%ju:%ju%n", &dev, &ino, &len) == 2
+ && (size_t)len == strlen(tmp)) {
+ if (fstat(1, &st) == 0 && st.st_dev == (dev_t)dev
+ && st.st_ino == (ino_t)ino)
+ sd_stdout_is_journal = true;
+ if (fstat(2, &st) == 0 && st.st_dev == (dev_t)dev
+ && st.st_ino == (ino_t)ino)
+ sd_stderr_is_journal = true;
+ }
+
+ /* these should *not* be passed to any other process we start */
+ unsetenv("WATCHDOG_PID");
+ unsetenv("WATCHDOG_USEC");
+}
diff --git a/lib/systemd.h b/lib/systemd.h
index d9885c5d9c..1933f4f688 100644
--- a/lib/systemd.h
+++ b/lib/systemd.h
@@ -28,9 +28,6 @@ extern "C" {
*
* Design point is that if systemd is not being used on this system
* then these functions becomes a no-op.
- *
- * To turn on systemd compilation, use --enable-systemd on
- * configure run.
*/
void systemd_send_stopping(void);
@@ -39,13 +36,18 @@ void systemd_send_stopping(void);
* the_process - Should we send watchdog if we are not the requested
* process?
*/
-void systemd_send_started(struct thread_master *master, int the_process);
+void systemd_send_started(struct thread_master *master);
/*
* status - A status string to send to systemd
*/
void systemd_send_status(const char *status);
+/*
+ * grab startup state from env vars
+ */
+void systemd_init_env(void);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/zclient.c b/lib/zclient.c
index 1b4ac080cd..0815e77d4e 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -1537,7 +1537,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_OPAQUE)) {
STREAM_GETW(s, api->opaque.length);
- assert(api->opaque.length < ZAPI_MESSAGE_OPAQUE_LENGTH);
+ assert(api->opaque.length <= ZAPI_MESSAGE_OPAQUE_LENGTH);
STREAM_GET(api->opaque.data, s, api->opaque.length);
}
@@ -3812,7 +3812,8 @@ static int zclient_read(struct thread *thread)
zclient->t_read = NULL;
/* Read zebra header (if we don't have it already). */
- if ((already = stream_get_endp(zclient->ibuf)) < ZEBRA_HEADER_SIZE) {
+ already = stream_get_endp(zclient->ibuf);
+ if (already < ZEBRA_HEADER_SIZE) {
ssize_t nbyte;
if (((nbyte = stream_read_try(zclient->ibuf, zclient->sock,
ZEBRA_HEADER_SIZE - already))
@@ -3825,7 +3826,6 @@ static int zclient_read(struct thread *thread)
return zclient_failed(zclient);
}
if (nbyte != (ssize_t)(ZEBRA_HEADER_SIZE - already)) {
- /* Try again later. */
zclient_event(ZCLIENT_READ, zclient);
return 0;
}
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..69be807c13 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,25 +710,12 @@ 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 = ospf6_route_create(o);
def->type = OSPF6_DEST_TYPE_NETWORK;
def->prefix.family = AF_INET6;
def->prefix.prefixlen = 0;
@@ -1163,10 +1150,11 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
/* (5),(6): the path preference is handled by the sorting
in the routing table. Always install the path by substituting
old route (if any). */
- route = ospf6_route_create();
+ route = ospf6_route_create(oa->ospf6);
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;
@@ -1250,7 +1237,9 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
listcount(old_route->nh_list));
}
} else {
- struct ospf6_route *tmp_route = ospf6_route_create();
+ struct ospf6_route *tmp_route;
+
+ tmp_route = ospf6_route_create(oa->ospf6);
ospf6_copy_nexthops(tmp_route->nh_list,
o_path->nh_list);
diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c
index 355b8441bd..f4d9964a57 100644
--- a/ospf6d/ospf6_area.c
+++ b/ospf6d/ospf6_area.c
@@ -519,7 +519,7 @@ DEFUN (area_range,
range = ospf6_route_lookup(&prefix, oa->range_table);
if (range == NULL) {
- range = ospf6_route_create();
+ range = ospf6_route_create(ospf6);
range->type = OSPF6_DEST_TYPE_RANGE;
range->prefix = prefix;
range->path.area_id = oa->area_id;
diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c
index d4e52f0ede..165e409eed 100644
--- a/ospf6d/ospf6_asbr.c
+++ b/ospf6d/ospf6_asbr.c
@@ -57,6 +57,7 @@
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_EXTERNAL_INFO, "OSPF6 ext. info");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_DIST_ARGS, "OSPF6 Distribute arguments");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_REDISTRIBUTE, "OSPF6 Redistribute arguments");
+DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_EXTERNAL_RT_AGGR, "OSPF6 ASBR Summarisation");
static void ospf6_asbr_redistribute_set(struct ospf6 *ospf6, int type);
static void ospf6_asbr_redistribute_unset(struct ospf6 *ospf6,
@@ -70,9 +71,28 @@ unsigned char conf_debug_ospf6_asbr = 0;
#define ZROUTE_NAME(x) zebra_route_string(x)
+/* Originate Type-5 and Type-7 LSA */
+static struct ospf6_lsa *ospf6_originate_type5_type7_lsas(
+ struct ospf6_route *route,
+ struct ospf6 *ospf6)
+{
+ struct ospf6_lsa *lsa;
+ struct listnode *lnode;
+ struct ospf6_area *oa = NULL;
+
+ lsa = ospf6_as_external_lsa_originate(route, ospf6);
+
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) {
+ if (IS_AREA_NSSA(oa))
+ ospf6_nssa_lsa_originate(route, oa);
+ }
+
+ return lsa;
+}
+
/* AS External LSA origination */
-void ospf6_as_external_lsa_originate(struct ospf6_route *route,
- struct ospf6 *ospf6)
+struct ospf6_lsa *ospf6_as_external_lsa_originate(struct ospf6_route *route,
+ struct ospf6 *ospf6)
{
char buffer[OSPF6_MAX_LSASIZE];
struct ospf6_lsa_header *lsa_header;
@@ -121,7 +141,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);
@@ -164,6 +184,8 @@ void ospf6_as_external_lsa_originate(struct ospf6_route *route,
/* Originate */
ospf6_lsa_originate_process(lsa, ospf6);
+
+ return lsa;
}
int ospf6_orig_as_external_lsa(struct thread *thread)
@@ -583,18 +605,18 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa)
}
}
- route = ospf6_route_create();
+ route = ospf6_route_create(ospf6);
route->type = OSPF6_DEST_TYPE_NETWORK;
route->prefix.family = AF_INET6;
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)) {
@@ -690,7 +712,7 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
if (ospf6_check_and_set_router_abr(ospf6))
oa = ospf6->backbone;
else
- oa = listgetdata(listhead(ospf6->area_list));
+ oa = listnode_head(ospf6->area_list);
}
if (oa == NULL) {
@@ -705,7 +727,7 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
return;
}
- route_to_del = ospf6_route_create();
+ route_to_del = ospf6_route_create(ospf6);
route_to_del->type = OSPF6_DEST_TYPE_NETWORK;
route_to_del->prefix.family = AF_INET6;
route_to_del->prefix.prefixlen = external->prefix.prefix_length;
@@ -1301,6 +1323,28 @@ void ospf6_asbr_remove_externals_from_area(struct ospf6_area *oa)
}
}
+static struct ospf6_external_aggr_rt *
+ospf6_external_aggr_match(struct ospf6 *ospf6, struct prefix *p)
+{
+ struct route_node *node;
+
+ node = route_node_match(ospf6->rt_aggr_tbl, p);
+ if (node == NULL)
+ return NULL;
+
+ if (IS_OSPF6_DEBUG_AGGR) {
+ struct ospf6_external_aggr_rt *ag = node->info;
+ zlog_debug("%s: Matching aggregator found.prefix: %pFX Aggregator %pFX",
+ __func__,
+ p,
+ &ag->p);
+ }
+
+ route_unlock_node(node);
+
+ return node->info;
+}
+
void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex,
struct prefix *prefix,
unsigned int nexthop_num,
@@ -1308,8 +1352,6 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex,
struct ospf6 *ospf6)
{
route_map_result_t ret;
- struct listnode *lnode;
- struct ospf6_area *oa;
struct ospf6_route troute;
struct ospf6_external_info tinfo;
struct ospf6_route *route, *match;
@@ -1378,6 +1420,7 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex,
match->path.cost = troute.path.cost;
else
match->path.cost = metric_value(ospf6, type, 0);
+
if (!IN6_IS_ADDR_UNSPECIFIED(&tinfo.forwarding))
memcpy(&info->forwarding, &tinfo.forwarding,
sizeof(struct in6_addr));
@@ -1414,25 +1457,22 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex,
}
match->path.origin.id = htonl(info->id);
- ospf6_as_external_lsa_originate(match, ospf6);
+ ospf6_handle_external_lsa_origination(ospf6, match, prefix);
+
ospf6_asbr_status_update(ospf6, ospf6->redistribute);
- for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) {
- if (IS_AREA_NSSA(oa))
- ospf6_nssa_lsa_originate(match, oa);
- }
return;
}
/* create new entry */
- route = ospf6_route_create();
+ route = ospf6_route_create(ospf6);
route->type = OSPF6_DEST_TYPE_NETWORK;
prefix_copy(&route->prefix, prefix);
+ route->ospf6 = ospf6;
info = (struct ospf6_external_info *)XCALLOC(
MTYPE_OSPF6_EXTERNAL_INFO, sizeof(struct ospf6_external_info));
route->route_option = info;
- info->id = ospf6->external_id++;
/* copy result of route-map */
if (ROUTEMAP(red)) {
@@ -1463,43 +1503,109 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex,
else
ospf6_route_add_nexthop(route, ifindex, NULL);
- /* create/update binding in external_id_table */
- prefix_id.family = AF_INET;
- prefix_id.prefixlen = IPV4_MAX_BITLEN;
- prefix_id.u.prefix4.s_addr = htonl(info->id);
- node = route_node_get(ospf6->external_id_table, &prefix_id);
- node->info = route;
-
route = ospf6_route_add(route, ospf6->external_table);
- route->route_option = info;
-
- if (IS_OSPF6_DEBUG_ASBR) {
- inet_ntop(AF_INET, &prefix_id.u.prefix4, ibuf, sizeof(ibuf));
- zlog_debug(
- "Advertise as AS-External Id:%s prefix %pFX metric %u",
- ibuf, prefix, route->path.metric_type);
- }
+ ospf6_handle_external_lsa_origination(ospf6, route, prefix);
- route->path.origin.id = htonl(info->id);
- ospf6_as_external_lsa_originate(route, ospf6);
ospf6_asbr_status_update(ospf6, ospf6->redistribute);
+
+}
+
+static void ospf6_asbr_external_lsa_remove_by_id(struct ospf6 *ospf6,
+ uint32_t id)
+{
+ struct ospf6_lsa *lsa;
+ struct ospf6_area *oa;
+ struct listnode *lnode;
+
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ htonl(id), ospf6->router_id, ospf6->lsdb);
+ if (!lsa)
+ return;
+
+ ospf6_external_lsa_purge(ospf6, lsa);
+
+ /* Delete the NSSA LSA */
for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) {
- if (IS_AREA_NSSA(oa))
- ospf6_nssa_lsa_originate(route, oa);
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_TYPE_7),
+ htonl(id), ospf6->router_id,
+ oa->lsdb);
+ if (lsa) {
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug("withdraw type 7 lsa, LS ID: %u",
+ htonl(id));
+
+ ospf6_lsa_purge(lsa);
+ }
}
+
+}
+
+static void
+ospf6_link_route_to_aggr(struct ospf6_external_aggr_rt *aggr,
+ struct ospf6_route *rt)
+{
+ hash_get(aggr->match_extnl_hash, rt, hash_alloc_intern);
+ rt->aggr_route = aggr;
+}
+
+static void
+ospf6_asbr_summary_remove_lsa_and_route(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr)
+{
+
+ /* Send a Max age LSA if it is already originated.*/
+ if (!CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED))
+ return;
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Flushing Aggregate route (%pFX)",
+ __func__,
+ &aggr->p);
+
+ ospf6_asbr_external_lsa_remove_by_id(ospf6, aggr->id);
+
+ if (aggr->route) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug(
+ "%s: Remove the blackhole route",
+ __func__);
+ ospf6_zebra_route_update_remove(aggr->route, ospf6);
+ ospf6_route_delete(aggr->route);
+ aggr->route = NULL;
+ }
+
+ aggr->id = 0;
+ /* Unset the Origination flag */
+ UNSET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED);
+}
+
+static void
+ospf6_unlink_route_from_aggr(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr,
+ struct ospf6_route *rt)
+{
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Unlinking external route(%pFX) from aggregator(%pFX), external route count:%ld",
+ __func__,
+ &rt->prefix,
+ &aggr->p,
+ OSPF6_EXTERNAL_RT_COUNT(aggr));
+
+ hash_release(aggr->match_extnl_hash, rt);
+ rt->aggr_route = NULL;
+
+ /* Flush the aggregate route if matching
+ * external route count becomes zero.
+ */
+ if (!OSPF6_EXTERNAL_RT_COUNT(aggr))
+ ospf6_asbr_summary_remove_lsa_and_route(ospf6, aggr);
}
void ospf6_asbr_redistribute_remove(int type, ifindex_t ifindex,
struct prefix *prefix, struct ospf6 *ospf6)
{
- struct ospf6_area *oa;
struct ospf6_route *match;
struct ospf6_external_info *info = NULL;
- struct listnode *lnode;
- struct route_node *node;
- struct ospf6_lsa *lsa;
- struct prefix prefix_id;
- char ibuf[16];
match = ospf6_route_lookup(prefix, ospf6->external_table);
if (match == NULL) {
@@ -1517,44 +1623,17 @@ void ospf6_asbr_redistribute_remove(int type, ifindex_t ifindex,
return;
}
- if (IS_OSPF6_DEBUG_ASBR) {
- inet_ntop(AF_INET, &prefix_id.u.prefix4, ibuf, sizeof(ibuf));
- zlog_debug("Withdraw %pFX (AS-External Id:%s)", prefix, ibuf);
- }
-
- lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
- htonl(info->id), ospf6->router_id, ospf6->lsdb);
- if (lsa) {
- if (IS_OSPF6_DEBUG_ASBR) {
- zlog_debug("withdraw type 5 LSA for route %pFX",
- prefix);
- }
- ospf6_lsa_purge(lsa);
- }
-
- /* Delete the NSSA LSA */
- for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) {
- lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_TYPE_7),
- htonl(info->id), ospf6->router_id,
- oa->lsdb);
- if (lsa) {
- if (IS_OSPF6_DEBUG_ASBR) {
- zlog_debug("withdraw type 7 LSA for route %pFX",
- prefix);
- }
- ospf6_lsa_purge(lsa);
- }
- }
+ /* This means aggregation on this route was not done, hence remove LSA
+ * if any originated for this prefix
+ */
+ if (!match->aggr_route)
+ ospf6_asbr_external_lsa_remove_by_id(ospf6, info->id);
+ else
+ ospf6_unlink_route_from_aggr(ospf6, match->aggr_route, match);
- /* remove binding in external_id_table */
- prefix_id.family = AF_INET;
- prefix_id.prefixlen = IPV4_MAX_BITLEN;
- prefix_id.u.prefix4.s_addr = htonl(info->id);
- node = route_node_lookup(ospf6->external_id_table, &prefix_id);
- assert(node);
- node->info = NULL;
- route_unlock_node(node); /* to free the lookup lock */
- route_unlock_node(node); /* to free the original lock */
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug("Removing route from external table %pFX",
+ prefix);
ospf6_route_remove(match, ospf6->external_table);
XFREE(MTYPE_OSPF6_EXTERNAL_INFO, info);
@@ -1574,6 +1653,7 @@ DEFUN (ospf6_redistribute,
VTY_DECLVAR_CONTEXT(ospf6, ospf6);
char *proto = argv[argc - 1]->text;
+
type = proto_redistnum(AFI_IP6, proto);
if (type < 0)
return CMD_WARNING_CONFIG_FAILED;
@@ -2568,36 +2648,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;
}
@@ -2608,3 +2693,988 @@ void install_element_ospf6_debug_asbr(void)
install_element(CONFIG_NODE, &debug_ospf6_asbr_cmd);
install_element(CONFIG_NODE, &no_debug_ospf6_asbr_cmd);
}
+
+/* ASBR Summarisation */
+void ospf6_fill_aggr_route_details(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr)
+{
+ struct ospf6_route *rt_aggr = aggr->route;
+ struct ospf6_external_info *ei_aggr = rt_aggr->route_option;
+
+ rt_aggr->prefix = aggr->p;
+ ei_aggr->tag = aggr->tag;
+ ei_aggr->type = 0;
+ ei_aggr->id = aggr->id;
+
+ /* When metric is not configured, apply the default metric */
+ rt_aggr->path.cost = ((aggr->metric == -1) ?
+ DEFAULT_DEFAULT_METRIC
+ : (unsigned int)(aggr->metric));
+ rt_aggr->path.metric_type = aggr->mtype;
+
+ rt_aggr->path.origin.id = htonl(aggr->id);
+}
+
+static void ospf6_originate_new_aggr_lsa(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr)
+{
+
+ struct prefix prefix_id;
+ struct route_node *node;
+ struct ospf6_lsa *lsa = NULL;
+ struct ospf6_route *rt_aggr;
+ struct ospf6_external_info *info;
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Originate new aggregate route(%pFX)", __func__,
+ &aggr->p);
+
+ aggr->id = ospf6->external_id++;
+ /* create/update binding in external_id_table */
+ prefix_id.family = AF_INET;
+ prefix_id.prefixlen = 32;
+ prefix_id.u.prefix4.s_addr = htonl(aggr->id);
+ node = route_node_get(ospf6->external_id_table, &prefix_id);
+ node->info = aggr;
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug(
+ "Advertise AS-External Id:%pI4 prefix %pFX metric %u",
+ &prefix_id.u.prefix4, &aggr->p, aggr->metric);
+
+ /* Create summary route and save it. */
+ rt_aggr = ospf6_route_create(ospf6);
+ rt_aggr->type = OSPF6_DEST_TYPE_NETWORK;
+ /* Needed to install route while calling zebra api */
+ SET_FLAG(rt_aggr->flag, OSPF6_ROUTE_BEST);
+
+ info = XCALLOC(MTYPE_OSPF6_EXTERNAL_INFO, sizeof(*info));
+ rt_aggr->route_option = info;
+ aggr->route = rt_aggr;
+
+ /* Prepare the external_info for aggregator
+ * Fill all the details which will get advertised
+ */
+ ospf6_fill_aggr_route_details(ospf6, aggr);
+
+ /* Add next-hop to Null interface. */
+ ospf6_add_route_nexthop_blackhole(rt_aggr);
+
+ ospf6_zebra_route_update_add(rt_aggr, ospf6);
+
+ /* Originate summary LSA */
+ lsa = ospf6_originate_type5_type7_lsas(rt_aggr, ospf6);
+ if (lsa) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Set the origination bit for aggregator",
+ __func__);
+ SET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED);
+ }
+}
+
+static void
+ospf6_aggr_handle_advertise_change(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr)
+{
+ /* Check if advertise option modified. */
+ if (CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE)) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Don't originate the summary address,It is configured to not-advertise.",
+ __func__);
+ ospf6_asbr_summary_remove_lsa_and_route(ospf6, aggr);
+
+ return;
+ }
+
+ /* There are no routes present under this aggregation config, hence
+ * nothing to originate here
+ */
+ if (OSPF6_EXTERNAL_RT_COUNT(aggr) == 0) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: No routes present under this aggregation",
+ __func__);
+ return;
+ }
+
+ if (!CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED)) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Now it is advertisable",
+ __func__);
+
+ ospf6_originate_new_aggr_lsa(ospf6, aggr);
+
+ return;
+ }
+}
+
+static void
+ospf6_originate_summary_lsa(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr,
+ struct ospf6_route *rt)
+{
+ struct ospf6_lsa *lsa = NULL, *aggr_lsa = NULL;
+ struct ospf6_external_info *info = NULL;
+ struct ospf6_external_aggr_rt *old_aggr;
+ struct ospf6_as_external_lsa *external;
+ struct ospf6_route *rt_aggr = NULL;
+ route_tag_t tag = 0;
+ unsigned int metric = 0;
+ int mtype;
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Prepare to originate Summary route(%pFX)",
+ __func__, &aggr->p);
+
+ /* This case to handle when the overlapping aggregator address
+ * is available. Best match will be considered.So need to delink
+ * from old aggregator and link to the new aggr.
+ */
+ if (rt->aggr_route) {
+ if (rt->aggr_route != aggr) {
+ old_aggr = rt->aggr_route;
+ ospf6_unlink_route_from_aggr(ospf6, old_aggr, rt);
+ }
+ }
+
+ /* Add the external route to hash table */
+ ospf6_link_route_to_aggr(aggr, rt);
+
+ /* The key for ID field is a running number and not prefix */
+ info = rt->route_option;
+ assert(info);
+ if (info->id) {
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ htonl(info->id), ospf6->router_id,
+ ospf6->lsdb);
+ assert(lsa);
+ }
+
+ aggr_lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ htonl(aggr->id), ospf6->router_id, ospf6->lsdb);
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Aggr LSA ID: %d flags %x.",
+ __func__, aggr->id, aggr->aggrflags);
+ /* Dont originate external LSA,
+ * If it is configured not to advertise.
+ */
+ if (CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE)) {
+ /* If it is already originated as external LSA,
+ * But, it is configured not to advertise then
+ * flush the originated external lsa.
+ */
+ if (lsa) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Purge the external LSA %s.",
+ __func__, lsa->name);
+ ospf6_external_lsa_purge(ospf6, lsa);
+ info->id = 0;
+ rt->path.origin.id = 0;
+ }
+
+ if (aggr_lsa) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Purge the aggr external LSA %s.",
+ __func__, lsa->name);
+ ospf6_asbr_summary_remove_lsa_and_route(ospf6, aggr);
+ }
+
+ UNSET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED);
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Don't originate the summary address,It is configured to not-advertise.",
+ __func__);
+ return;
+ }
+
+ /* Summary route already originated,
+ * So, Do nothing.
+ */
+ if (CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED)) {
+ if (!aggr_lsa) {
+ zlog_warn(
+ "%s: Could not refresh/originate %pFX",
+ __func__,
+ &aggr->p);
+ /* Remove the assert later */
+ assert(aggr_lsa);
+ return;
+ }
+
+ external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END
+ (aggr_lsa->header);
+ metric = (unsigned long)OSPF6_ASBR_METRIC(external);
+ tag = ospf6_as_external_lsa_get_tag(aggr_lsa);
+ mtype = CHECK_FLAG(external->bits_metric,
+ OSPF6_ASBR_BIT_E) ? 2 : 1;
+
+ /* Prepare the external_info for aggregator */
+ ospf6_fill_aggr_route_details(ospf6, aggr);
+ rt_aggr = aggr->route;
+ /* If tag/metric/metric-type modified , then re-originate the
+ * route with modified tag/metric/metric-type details.
+ */
+ if ((tag != aggr->tag)
+ || (metric != (unsigned int)rt_aggr->path.cost)
+ || (mtype != aggr->mtype)) {
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug(
+ "%s: Routetag(old:%d new:%d)/Metric(o:%u,n:%u)/mtype(o:%d n:%d) modified,So refresh the summary route.(%pFX)",
+ __func__, tag, aggr->tag,
+ metric,
+ aggr->metric,
+ mtype, aggr->mtype,
+ &aggr->p);
+
+ aggr_lsa = ospf6_originate_type5_type7_lsas(aggr->route,
+ ospf6);
+ if (aggr_lsa)
+ SET_FLAG(aggr->aggrflags,
+ OSPF6_EXTERNAL_AGGRT_ORIGINATED);
+ }
+
+ return;
+ }
+
+ /* If the external route prefix same as aggregate route
+ * and if external route is already originated as TYPE-5
+ * then it need to be refreshed and originate bit should
+ * be set.
+ */
+ if (lsa && prefix_same(&aggr->p, &rt->prefix)) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: External route prefix is same as aggr so refreshing LSA(%pFX)",
+ __PRETTY_FUNCTION__,
+ &aggr->p);
+
+ THREAD_OFF(lsa->refresh);
+ thread_add_event(master, ospf6_lsa_refresh, lsa, 0,
+ &lsa->refresh);
+ aggr->id = info->id;
+ SET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED);
+ return;
+ }
+
+ ospf6_originate_new_aggr_lsa(ospf6, aggr);
+}
+
+static void ospf6_aggr_handle_external_info(void *data)
+{
+ struct ospf6_route *rt = (struct ospf6_route *)data;
+ struct ospf6_external_aggr_rt *aggr = NULL;
+ struct ospf6_lsa *lsa = NULL;
+ struct ospf6_external_info *info;
+ struct ospf6 *ospf6 = NULL;
+ struct prefix prefix_id;
+ struct route_node *node;
+
+ rt->aggr_route = NULL;
+
+ rt->to_be_processed = true;
+
+ if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE(AS_EXTERNAL))
+ zlog_debug("%s: Handle external route for origination/refresh (%pFX)",
+ __func__,
+ &rt->prefix);
+
+ ospf6 = rt->ospf6;
+ assert(ospf6);
+
+ aggr = ospf6_external_aggr_match(ospf6,
+ &rt->prefix);
+ if (aggr) {
+ ospf6_originate_summary_lsa(ospf6, aggr, rt);
+ return;
+ }
+
+ info = rt->route_option;
+ if (info->id) {
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ htonl(info->id), ospf6->router_id,
+ ospf6->lsdb);
+ if (lsa) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: LSA found, refresh it",
+ __func__);
+ THREAD_OFF(lsa->refresh);
+ thread_add_event(master, ospf6_lsa_refresh, lsa, 0,
+ &lsa->refresh);
+ return;
+ }
+ }
+
+ info->id = ospf6->external_id++;
+ rt->path.origin.id = htonl(info->id);
+
+ /* create/update binding in external_id_table */
+ prefix_id.family = AF_INET;
+ prefix_id.prefixlen = 32;
+ prefix_id.u.prefix4.s_addr = htonl(info->id);
+ node = route_node_get(ospf6->external_id_table, &prefix_id);
+ node->info = rt;
+
+ (void)ospf6_originate_type5_type7_lsas(rt, ospf6);
+}
+
+static void
+ospf6_asbr_summary_config_delete(struct ospf6 *ospf6, struct route_node *rn)
+{
+ struct ospf6_external_aggr_rt *aggr = rn->info;
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Deleting Aggregate route (%pFX)",
+ __func__,
+ &aggr->p);
+
+ ospf6_asbr_summary_remove_lsa_and_route(ospf6, aggr);
+
+ rn->info = NULL;
+ route_unlock_node(rn);
+}
+
+static int
+ospf6_handle_external_aggr_modify(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr)
+{
+ struct ospf6_lsa *lsa = NULL;
+ struct ospf6_as_external_lsa *asel = NULL;
+ struct ospf6_route *rt_aggr;
+ unsigned int metric = 0;
+ route_tag_t tag = 0;
+ int mtype;
+
+ lsa = ospf6_lsdb_lookup(
+ htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ htonl(aggr->id), ospf6->router_id,
+ ospf6->lsdb);
+ if (!lsa) {
+ zlog_warn(
+ "%s: Could not refresh/originate %pFX",
+ __func__,
+ &aggr->p);
+
+ return OSPF6_FAILURE;
+ }
+
+ asel = (struct ospf6_as_external_lsa *)
+ OSPF6_LSA_HEADER_END(lsa->header);
+ metric = (unsigned long)OSPF6_ASBR_METRIC(asel);
+ tag = ospf6_as_external_lsa_get_tag(lsa);
+ mtype = CHECK_FLAG(asel->bits_metric,
+ OSPF6_ASBR_BIT_E) ? 2 : 1;
+
+ /* Fill all the details for advertisement */
+ ospf6_fill_aggr_route_details(ospf6, aggr);
+ rt_aggr = aggr->route;
+ /* If tag/metric/metric-type modified , then
+ * re-originate the route with modified
+ * tag/metric/metric-type details.
+ */
+ if ((tag != aggr->tag)
+ || (metric
+ != (unsigned int)rt_aggr->path.cost)
+ || (mtype
+ != aggr->mtype)) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug(
+ "%s: Changed tag(old:%d new:%d)/metric(o:%u n:%d)/mtype(o:%d n:%d),So refresh the summary route.(%pFX)",
+ __func__, tag,
+ aggr->tag,
+ metric,
+ (unsigned int)rt_aggr->path.cost,
+ mtype, aggr->mtype,
+ &aggr->p);
+
+ (void)ospf6_originate_type5_type7_lsas(
+ aggr->route,
+ ospf6);
+ }
+
+ return OSPF6_SUCCESS;
+}
+
+static void ospf6_handle_external_aggr_update(struct ospf6 *ospf6)
+{
+ struct route_node *rn = NULL;
+ int ret;
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Process modified aggregators.", __func__);
+
+ for (rn = route_top(ospf6->rt_aggr_tbl); rn; rn = route_next(rn)) {
+ struct ospf6_external_aggr_rt *aggr;
+
+ if (!rn->info)
+ continue;
+
+ aggr = rn->info;
+
+ if (aggr->action == OSPF6_ROUTE_AGGR_DEL) {
+ aggr->action = OSPF6_ROUTE_AGGR_NONE;
+ ospf6_asbr_summary_config_delete(ospf6, rn);
+
+ if (OSPF6_EXTERNAL_RT_COUNT(aggr))
+ hash_clean(aggr->match_extnl_hash,
+ ospf6_aggr_handle_external_info);
+
+ hash_free(aggr->match_extnl_hash);
+ XFREE(MTYPE_OSPF6_EXTERNAL_RT_AGGR, aggr);
+
+ } else if (aggr->action == OSPF6_ROUTE_AGGR_MODIFY) {
+
+ aggr->action = OSPF6_ROUTE_AGGR_NONE;
+
+ /* Check if tag/metric/metric-type modified */
+ if (CHECK_FLAG(aggr->aggrflags,
+ OSPF6_EXTERNAL_AGGRT_ORIGINATED)
+ && !CHECK_FLAG(aggr->aggrflags,
+ OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE)) {
+
+ ret = ospf6_handle_external_aggr_modify(ospf6,
+ aggr);
+ if (ret == OSPF6_FAILURE)
+ continue;
+ }
+
+ /* Advertise option modified ?
+ * If so, handled it here.
+ */
+ ospf6_aggr_handle_advertise_change(ospf6, aggr);
+ }
+ }
+}
+
+static void ospf6_aggr_unlink_external_info(void *data)
+{
+ struct ospf6_route *rt = (struct ospf6_route *)data;
+
+ rt->aggr_route = NULL;
+
+ rt->to_be_processed = true;
+}
+
+void ospf6_external_aggregator_free(struct ospf6_external_aggr_rt *aggr)
+{
+ if (OSPF6_EXTERNAL_RT_COUNT(aggr))
+ hash_clean(aggr->match_extnl_hash,
+ ospf6_aggr_unlink_external_info);
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Release the aggregator Address(%pFX)",
+ __func__,
+ &aggr->p);
+
+ hash_free(aggr->match_extnl_hash);
+ aggr->match_extnl_hash = NULL;
+
+ XFREE(MTYPE_OSPF6_EXTERNAL_RT_AGGR, aggr);
+}
+
+static void
+ospf6_delete_all_marked_aggregators(struct ospf6 *ospf6)
+{
+ struct route_node *rn = NULL;
+ struct ospf6_external_aggr_rt *aggr;
+
+ /* Loop through all the aggregators, Delete all aggregators
+ * which are marked as DELETE. Set action to NONE for remaining
+ * aggregators
+ */
+ for (rn = route_top(ospf6->rt_aggr_tbl); rn; rn = route_next(rn)) {
+ if (!rn->info)
+ continue;
+
+ aggr = rn->info;
+
+ if (aggr->action != OSPF6_ROUTE_AGGR_DEL) {
+ aggr->action = OSPF6_ROUTE_AGGR_NONE;
+ continue;
+ }
+ ospf6_asbr_summary_config_delete(ospf6, rn);
+ ospf6_external_aggregator_free(aggr);
+ }
+}
+
+static void ospf6_handle_exnl_rt_after_aggr_del(struct ospf6 *ospf6,
+ struct ospf6_route *rt)
+{
+ struct ospf6_lsa *lsa;
+
+ /* Process only marked external routes.
+ * These routes were part of a deleted
+ * aggregator.So, originate now.
+ */
+ if (!rt->to_be_processed)
+ return;
+
+ rt->to_be_processed = false;
+
+ lsa = ospf6_find_external_lsa(ospf6, &rt->prefix);
+
+ if (lsa) {
+ THREAD_OFF(lsa->refresh);
+ thread_add_event(master, ospf6_lsa_refresh, lsa, 0,
+ &lsa->refresh);
+ } else {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Originate external route(%pFX)",
+ __func__,
+ &rt->prefix);
+
+ (void)ospf6_originate_type5_type7_lsas(rt, ospf6);
+ }
+}
+
+static void ospf6_handle_aggregated_exnl_rt(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr,
+ struct ospf6_route *rt)
+{
+ struct ospf6_lsa *lsa;
+ struct ospf6_as_external_lsa *ext_lsa;
+ struct ospf6_external_info *info;
+
+ /* Handling the case where the external route prefix
+ * and aggegate prefix is same
+ * If same dont flush the originated external LSA.
+ */
+ if (prefix_same(&aggr->p, &rt->prefix)) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: External Route prefix same as Aggregator(%pFX), so dont flush.",
+ __func__,
+ &rt->prefix);
+
+ return;
+ }
+
+ info = rt->route_option;
+ assert(info);
+
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ htonl(info->id), ospf6->router_id, ospf6->lsdb);
+ if (lsa) {
+ ext_lsa = (struct ospf6_as_external_lsa
+ *)((char *)(lsa->header)
+ + sizeof(struct ospf6_lsa_header));
+
+ if (rt->prefix.prefixlen != ext_lsa->prefix.prefix_length)
+ return;
+
+ ospf6_external_lsa_purge(ospf6, lsa);
+
+ /* Resetting the ID of route */
+ rt->path.origin.id = 0;
+ info->id = 0;
+ }
+}
+
+static void
+ospf6_handle_external_aggr_add(struct ospf6 *ospf6)
+{
+ struct ospf6_route *rt = NULL;
+ struct ospf6_external_info *ei = NULL;
+ struct ospf6_external_aggr_rt *aggr;
+
+ /* Delete all the aggregators which are marked as
+ * OSPF6_ROUTE_AGGR_DEL.
+ */
+ ospf6_delete_all_marked_aggregators(ospf6);
+
+ for (rt = ospf6_route_head(ospf6->external_table); rt;
+ rt = ospf6_route_next(rt)) {
+ ei = rt->route_option;
+ if (ei == NULL)
+ continue;
+
+ if (is_default_prefix(&rt->prefix))
+ continue;
+
+ aggr = ospf6_external_aggr_match(ospf6,
+ &rt->prefix);
+
+ /* If matching aggregator found, Add
+ * the external route refrenace to the
+ * aggregator and originate the aggr
+ * route if it is advertisable.
+ * flush the external LSA if it is
+ * already originated for this external
+ * prefix.
+ */
+ if (aggr) {
+ ospf6_originate_summary_lsa(ospf6, aggr, rt);
+
+ /* All aggregated external rts
+ * are handled here.
+ */
+ ospf6_handle_aggregated_exnl_rt(
+ ospf6, aggr, rt);
+ continue;
+ }
+
+ /* External routes which are only out
+ * of aggregation will be handled here.
+ */
+ ospf6_handle_exnl_rt_after_aggr_del(
+ ospf6, rt);
+ }
+}
+
+static int ospf6_asbr_summary_process(struct thread *thread)
+{
+ struct ospf6 *ospf6 = THREAD_ARG(thread);
+ int operation = 0;
+
+ ospf6->t_external_aggr = NULL;
+ operation = ospf6->aggr_action;
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: operation:%d",
+ __func__,
+ operation);
+
+ switch (operation) {
+ case OSPF6_ROUTE_AGGR_ADD:
+ ospf6_handle_external_aggr_add(ospf6);
+ break;
+ case OSPF6_ROUTE_AGGR_DEL:
+ case OSPF6_ROUTE_AGGR_MODIFY:
+ ospf6_handle_external_aggr_update(ospf6);
+ break;
+ default:
+ break;
+ }
+
+ return OSPF6_SUCCESS;
+}
+
+static void
+ospf6_start_asbr_summary_delay_timer(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr,
+ ospf6_aggr_action_t operation)
+{
+ aggr->action = operation;
+
+ if (ospf6->t_external_aggr) {
+ if (ospf6->aggr_action == OSPF6_ROUTE_AGGR_ADD) {
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Not required to restart timer,set is already added.",
+ __func__);
+ return;
+ }
+
+ if (operation == OSPF6_ROUTE_AGGR_ADD) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s, Restarting Aggregator delay timer.",
+ __func__);
+ THREAD_OFF(ospf6->t_external_aggr);
+ }
+ }
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Start Aggregator delay timer %d(in seconds).",
+ __func__, ospf6->aggr_delay_interval);
+
+ ospf6->aggr_action = operation;
+ thread_add_timer(master,
+ ospf6_asbr_summary_process,
+ ospf6, ospf6->aggr_delay_interval,
+ &ospf6->t_external_aggr);
+}
+
+int ospf6_asbr_external_rt_advertise(struct ospf6 *ospf6,
+ struct prefix *p)
+{
+ struct route_node *rn;
+ struct ospf6_external_aggr_rt *aggr;
+
+ rn = route_node_lookup(ospf6->rt_aggr_tbl, p);
+ if (!rn)
+ return OSPF6_INVALID;
+
+ aggr = rn->info;
+
+ route_unlock_node(rn);
+
+ if (!CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE))
+ return OSPF6_INVALID;
+
+ UNSET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE);
+
+ if (!OSPF6_EXTERNAL_RT_COUNT(aggr))
+ return OSPF6_SUCCESS;
+
+ ospf6_start_asbr_summary_delay_timer(ospf6, aggr,
+ OSPF6_ROUTE_AGGR_MODIFY);
+
+ return OSPF6_SUCCESS;
+}
+
+int ospf6_external_aggr_delay_timer_set(struct ospf6 *ospf6,
+ unsigned int interval)
+{
+ ospf6->aggr_delay_interval = interval;
+
+ return OSPF6_SUCCESS;
+}
+
+static unsigned int ospf6_external_rt_hash_key(const void *data)
+{
+ const struct ospf6_route *rt = data;
+ unsigned int key = 0;
+
+ key = prefix_hash_key(&rt->prefix);
+ return key;
+}
+
+static bool ospf6_external_rt_hash_cmp(const void *d1, const void *d2)
+{
+ const struct ospf6_route *rt1 = d1;
+ const struct ospf6_route *rt2 = d2;
+
+ return prefix_same(&rt1->prefix, &rt2->prefix);
+}
+
+static struct ospf6_external_aggr_rt *
+ospf6_external_aggr_new(struct prefix *p)
+{
+ struct ospf6_external_aggr_rt *aggr;
+
+ aggr = XCALLOC(MTYPE_OSPF6_EXTERNAL_RT_AGGR,
+ sizeof(struct ospf6_external_aggr_rt));
+
+ prefix_copy(&aggr->p, p);
+ aggr->metric = -1;
+ aggr->mtype = DEFAULT_METRIC_TYPE;
+ aggr->match_extnl_hash = hash_create(ospf6_external_rt_hash_key,
+ ospf6_external_rt_hash_cmp,
+ "Ospf6 external route hash");
+ return aggr;
+}
+
+static void ospf6_external_aggr_add(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr)
+{
+ struct route_node *rn;
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Adding Aggregate route to Aggr table (%pFX)",
+ __func__,
+ &aggr->p);
+
+ rn = route_node_get(ospf6->rt_aggr_tbl, &aggr->p);
+ if (rn->info)
+ route_unlock_node(rn);
+ else
+ rn->info = aggr;
+}
+
+int ospf6_asbr_external_rt_no_advertise(struct ospf6 *ospf6,
+ struct prefix *p)
+{
+ struct ospf6_external_aggr_rt *aggr;
+ route_tag_t tag = 0;
+
+ aggr = ospf6_external_aggr_config_lookup(ospf6, p);
+ if (aggr) {
+ if (CHECK_FLAG(aggr->aggrflags,
+ OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE))
+ return OSPF6_SUCCESS;
+
+ SET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE);
+
+ aggr->tag = tag;
+ aggr->metric = -1;
+
+ if (!OSPF6_EXTERNAL_RT_COUNT(aggr))
+ return OSPF6_SUCCESS;
+
+ ospf6_start_asbr_summary_delay_timer(ospf6, aggr,
+ OSPF6_ROUTE_AGGR_MODIFY);
+ } else {
+ aggr = ospf6_external_aggr_new(p);
+
+ if (!aggr)
+ return OSPF6_FAILURE;
+
+ SET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE);
+ ospf6_external_aggr_add(ospf6, aggr);
+ ospf6_start_asbr_summary_delay_timer(ospf6, aggr,
+ OSPF6_ROUTE_AGGR_ADD);
+ }
+
+ return OSPF6_SUCCESS;
+}
+
+struct ospf6_external_aggr_rt *
+ospf6_external_aggr_config_lookup(struct ospf6 *ospf6, struct prefix *p)
+{
+ struct route_node *rn;
+
+ rn = route_node_lookup(ospf6->rt_aggr_tbl, p);
+ if (rn) {
+ route_unlock_node(rn);
+ return rn->info;
+ }
+
+ return NULL;
+}
+
+
+int ospf6_external_aggr_config_set(struct ospf6 *ospf6, struct prefix *p,
+ route_tag_t tag, int metric, int mtype)
+{
+ struct ospf6_external_aggr_rt *aggregator;
+
+ aggregator = ospf6_external_aggr_config_lookup(ospf6, p);
+
+ if (aggregator) {
+ if (CHECK_FLAG(aggregator->aggrflags,
+ OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE))
+ UNSET_FLAG(aggregator->aggrflags,
+ OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE);
+ else if ((aggregator->tag == tag)
+ && (aggregator->metric == metric)
+ && (aggregator->mtype == mtype))
+ return OSPF6_SUCCESS;
+
+ aggregator->tag = tag;
+ aggregator->metric = metric;
+ aggregator->mtype = mtype;
+
+ ospf6_start_asbr_summary_delay_timer(ospf6, aggregator,
+ OSPF6_ROUTE_AGGR_MODIFY);
+ } else {
+ aggregator = ospf6_external_aggr_new(p);
+ if (!aggregator)
+ return OSPF6_FAILURE;
+
+ aggregator->tag = tag;
+ aggregator->metric = metric;
+ aggregator->mtype = mtype;
+
+ ospf6_external_aggr_add(ospf6, aggregator);
+ ospf6_start_asbr_summary_delay_timer(ospf6, aggregator,
+ OSPF6_ROUTE_AGGR_ADD);
+ }
+
+ return OSPF6_SUCCESS;
+}
+
+int ospf6_external_aggr_config_unset(struct ospf6 *ospf6,
+ struct prefix *p)
+{
+ struct route_node *rn;
+ struct ospf6_external_aggr_rt *aggr;
+
+ rn = route_node_lookup(ospf6->rt_aggr_tbl, p);
+ if (!rn)
+ return OSPF6_INVALID;
+
+ aggr = rn->info;
+
+ route_unlock_node(rn);
+
+ if (!OSPF6_EXTERNAL_RT_COUNT(aggr)) {
+ ospf6_asbr_summary_config_delete(ospf6, rn);
+ ospf6_external_aggregator_free(aggr);
+ return OSPF6_SUCCESS;
+ }
+
+ ospf6_start_asbr_summary_delay_timer(ospf6, aggr,
+ OSPF6_ROUTE_AGGR_DEL);
+
+ return OSPF6_SUCCESS;
+}
+
+void ospf6_handle_external_lsa_origination(struct ospf6 *ospf6,
+ struct ospf6_route *rt,
+ struct prefix *p)
+{
+
+ struct ospf6_external_aggr_rt *aggr;
+ struct ospf6_external_info *info;
+ struct prefix prefix_id;
+ struct route_node *node;
+
+ if (!is_default_prefix(p)) {
+ aggr = ospf6_external_aggr_match(ospf6,
+ p);
+
+ if (aggr) {
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Send Aggregate LSA (%pFX)",
+ __func__,
+ &aggr->p);
+
+ ospf6_originate_summary_lsa(
+ ospf6, aggr, rt);
+
+ /* Handling the case where the
+ * external route prefix
+ * and aggegate prefix is same
+ * If same dont flush the
+ * originated
+ * external LSA.
+ */
+ ospf6_handle_aggregated_exnl_rt(
+ ospf6, aggr, rt);
+ return;
+ }
+ }
+
+ info = rt->route_option;
+
+ /* When the info->id = 0, it means it is being originated for the
+ * first time.
+ */
+ if (!info->id) {
+ info->id = ospf6->external_id++;
+
+ /* create/update binding in external_id_table */
+ prefix_id.family = AF_INET;
+ prefix_id.prefixlen = 32;
+ prefix_id.u.prefix4.s_addr = htonl(info->id);
+ node = route_node_get(ospf6->external_id_table, &prefix_id);
+ node->info = rt;
+
+ } else {
+ prefix_id.family = AF_INET;
+ prefix_id.prefixlen = 32;
+ prefix_id.u.prefix4.s_addr = htonl(info->id);
+ }
+
+ rt->path.origin.id = htonl(info->id);
+
+ if (IS_OSPF6_DEBUG_ASBR) {
+ zlog_debug("Advertise new AS-External Id:%pI4 prefix %pFX metric %u",
+ &prefix_id.u.prefix4, p, rt->path.metric_type);
+ }
+
+ ospf6_originate_type5_type7_lsas(rt, ospf6);
+
+}
+
+void ospf6_unset_all_aggr_flag(struct ospf6 *ospf6)
+{
+ struct route_node *rn = NULL;
+ struct ospf6_external_aggr_rt *aggr;
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("Unset the origination bit for all aggregator");
+
+ /* Resetting the running external ID counter so that the origination
+ * of external LSAs starts from the beginning 0.0.0.1
+ */
+ ospf6->external_id = OSPF6_EXT_INIT_LS_ID;
+
+ for (rn = route_top(ospf6->rt_aggr_tbl); rn; rn = route_next(rn)) {
+ if (!rn->info)
+ continue;
+
+ aggr = rn->info;
+
+ UNSET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED);
+ }
+}
diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h
index 7ccd1c992b..0aa1374a46 100644
--- a/ospf6d/ospf6_asbr.h
+++ b/ospf6d/ospf6_asbr.h
@@ -46,6 +46,52 @@ struct ospf6_external_info {
route_tag_t tag;
ifindex_t ifindex;
+
+};
+
+/* OSPF6 ASBR Summarisation */
+typedef enum {
+ OSPF6_ROUTE_AGGR_NONE = 0,
+ OSPF6_ROUTE_AGGR_ADD,
+ OSPF6_ROUTE_AGGR_DEL,
+ OSPF6_ROUTE_AGGR_MODIFY
+} ospf6_aggr_action_t;
+
+#define OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE 0x1
+#define OSPF6_EXTERNAL_AGGRT_ORIGINATED 0x2
+
+#define OSPF6_EXTERNAL_RT_COUNT(aggr) \
+ (((struct ospf6_external_aggr_rt *)aggr)->match_extnl_hash->count)
+
+struct ospf6_external_aggr_rt {
+ /* range address and masklen */
+ struct prefix p;
+
+ /* use bits for OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE and
+ * OSPF6_EXTERNAL_AGGRT_ORIGINATED
+ */
+ uint16_t aggrflags;
+
+ /* To store external metric-type */
+ uint8_t mtype;
+
+ /* Route tag for summary address */
+ route_tag_t tag;
+
+ /* To store aggregated metric config */
+ int metric;
+
+ /* To Store the LS ID when LSA is originated */
+ uint32_t id;
+
+ /* Action to be done after delay timer expiry */
+ int action;
+
+ /* OSPFv3 route generated by summary address. */
+ struct ospf6_route *route;
+
+ /* Hash table of matching external routes */
+ struct hash *match_extnl_hash;
};
/* AS-External-LSA */
@@ -110,8 +156,31 @@ extern void ospf6_asbr_distribute_list_update(struct ospf6 *ospf6,
struct ospf6_redist *ospf6_redist_lookup(struct ospf6 *ospf6, int type,
unsigned short instance);
extern void ospf6_asbr_routemap_update(const char *mapname);
-extern void ospf6_as_external_lsa_originate(struct ospf6_route *route,
- struct ospf6 *ospf6);
+extern struct ospf6_lsa *
+ospf6_as_external_lsa_originate(struct ospf6_route *route,
+ struct ospf6 *ospf6);
extern void ospf6_asbr_status_update(struct ospf6 *ospf6, int status);
+int ospf6_asbr_external_rt_advertise(struct ospf6 *ospf6,
+ struct prefix *p);
+int ospf6_external_aggr_delay_timer_set(struct ospf6 *ospf6,
+ unsigned int interval);
+int ospf6_asbr_external_rt_no_advertise(struct ospf6 *ospf6,
+ struct prefix *p);
+
+struct ospf6_external_aggr_rt *
+ospf6_external_aggr_config_lookup(struct ospf6 *ospf6, struct prefix *p);
+
+int ospf6_external_aggr_config_set(struct ospf6 *ospf6, struct prefix *p,
+ route_tag_t tag, int metric, int mtype);
+
+int ospf6_external_aggr_config_unset(struct ospf6 *ospf6,
+ struct prefix *p);
+void ospf6_handle_external_lsa_origination(struct ospf6 *ospf6,
+ struct ospf6_route *rt,
+ struct prefix *p);
+void ospf6_external_aggregator_free(struct ospf6_external_aggr_rt *aggr);
+void ospf6_unset_all_aggr_flag(struct ospf6 *ospf6);
+void ospf6_fill_aggr_route_details(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr);
#endif /* OSPF6_ASBR_H */
diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c
index 05b43189e9..3d52597161 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);
@@ -106,7 +116,7 @@ void ospf6_lsa_originate(struct ospf6_lsa *lsa)
lsdb_self = ospf6_get_scoped_lsdb_self(lsa);
ospf6_lsdb_add(ospf6_lsa_copy(lsa), lsdb_self);
- lsa->refresh = NULL;
+ THREAD_OFF(lsa->refresh);
thread_add_timer(master, ospf6_lsa_refresh, lsa, OSPF_LS_REFRESH_TIME,
&lsa->refresh);
@@ -139,6 +149,31 @@ void ospf6_lsa_originate_interface(struct ospf6_lsa *lsa,
ospf6_lsa_originate(lsa);
}
+void ospf6_remove_id_from_external_id_table(struct ospf6 *ospf6,
+ uint32_t id)
+{
+ struct prefix prefix_id;
+ struct route_node *node;
+
+ /* remove binding in external_id_table */
+ prefix_id.family = AF_INET;
+ prefix_id.prefixlen = 32;
+ prefix_id.u.prefix4.s_addr = id;
+ node = route_node_lookup(ospf6->external_id_table, &prefix_id);
+ assert(node);
+ node->info = NULL;
+ route_unlock_node(node); /* to free the lookup lock */
+ route_unlock_node(node); /* to free the original lock */
+
+}
+
+void ospf6_external_lsa_purge(struct ospf6 *ospf6, struct ospf6_lsa *lsa)
+{
+ ospf6_lsa_purge(lsa);
+
+ ospf6_remove_id_from_external_id_table(ospf6, lsa->header->id);
+}
+
void ospf6_lsa_purge(struct ospf6_lsa *lsa)
{
struct ospf6_lsa *self;
@@ -299,7 +334,7 @@ void ospf6_flood_interface(struct ospf6_neighbor *from, struct ospf6_lsa *lsa,
{
struct listnode *node, *nnode;
struct ospf6_neighbor *on;
- struct ospf6_lsa *req;
+ struct ospf6_lsa *req, *old;
int retrans_added = 0;
int is_debug = 0;
@@ -408,12 +443,27 @@ void ospf6_flood_interface(struct ospf6_neighbor *from, struct ospf6_lsa *lsa,
if (is_debug)
zlog_debug("Add retrans-list of neighbor %s ",
on->name);
- ospf6_increment_retrans_count(lsa);
- ospf6_lsdb_add(ospf6_lsa_copy(lsa), on->retrans_list);
- thread_add_timer(master, ospf6_lsupdate_send_neighbor,
- on, on->ospf6_if->rxmt_interval,
- &on->thread_send_lsupdate);
- retrans_added++;
+
+ /* Do not increment the retrans count if the lsa is
+ * already present in the retrans list.
+ */
+ old = ospf6_lsdb_lookup(
+ lsa->header->type, lsa->header->id,
+ lsa->header->adv_router, on->retrans_list);
+ if (!old) {
+ if (is_debug)
+ zlog_debug(
+ "Increment %s from retrans_list of %s",
+ lsa->name, on->name);
+ ospf6_increment_retrans_count(lsa);
+ ospf6_lsdb_add(ospf6_lsa_copy(lsa),
+ on->retrans_list);
+ thread_add_timer(
+ master, ospf6_lsupdate_send_neighbor,
+ on, on->ospf6_if->rxmt_interval,
+ &on->thread_send_lsupdate);
+ retrans_added++;
+ }
}
}
diff --git a/ospf6d/ospf6_flood.h b/ospf6d/ospf6_flood.h
index 5515a1c3fe..4e4fc55ed4 100644
--- a/ospf6d/ospf6_flood.h
+++ b/ospf6d/ospf6_flood.h
@@ -39,6 +39,9 @@ extern void ospf6_lsa_originate_area(struct ospf6_lsa *lsa,
struct ospf6_area *oa);
extern void ospf6_lsa_originate_interface(struct ospf6_lsa *lsa,
struct ospf6_interface *oi);
+void ospf6_remove_id_from_external_id_table(struct ospf6 *ospf6,
+ uint32_t id);
+void ospf6_external_lsa_purge(struct ospf6 *ospf6, struct ospf6_lsa *lsa);
extern void ospf6_lsa_purge(struct ospf6_lsa *lsa);
extern void ospf6_lsa_purge_multi_ls_id(struct ospf6_area *oa,
diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c
index 1e75fc60f6..ec672d8f3f 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,
@@ -448,7 +435,7 @@ void ospf6_interface_connected_route_update(struct interface *ifp)
}
}
- route = ospf6_route_create();
+ route = ospf6_route_create(oi->area->ospf6);
memcpy(&route->prefix, c->address, sizeof(struct prefix));
apply_mask(&route->prefix);
route->type = OSPF6_DEST_TYPE_NETWORK;
@@ -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..e4db8f3a02 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)) {
@@ -1347,7 +1327,7 @@ int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread)
|| current + OSPF6_PREFIX_SIZE(op) > end)
break;
- route = ospf6_route_create();
+ route = ospf6_route_create(oi->area->ospf6);
route->type = OSPF6_DEST_TYPE_NETWORK;
route->prefix.family = AF_INET6;
@@ -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));
@@ -1810,19 +1790,19 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa)
continue;
}
- route = ospf6_route_create();
+ route = ospf6_route_create(oa->ospf6);
memset(&route->prefix, 0, sizeof(struct prefix));
route->prefix.family = AF_INET6;
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_lsa.c b/ospf6d/ospf6_lsa.c
index bab5fdaae8..9c03ce21ed 100644
--- a/ospf6d/ospf6_lsa.c
+++ b/ospf6d/ospf6_lsa.c
@@ -45,6 +45,10 @@
#include "ospf6_flood.h"
#include "ospf6d.h"
+#ifndef VTYSH_EXTRACT_PL
+#include "ospf6d/ospf6_lsa_clippy.c"
+#endif
+
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA, "OSPF6 LSA");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA_HEADER, "OSPF6 LSA header");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA_SUMMARY, "OSPF6 LSA summary");
@@ -822,6 +826,8 @@ int ospf6_lsa_expire(struct thread *thread)
if (CHECK_FLAG(lsa->flag, OSPF6_LSA_HEADERONLY))
return 0; /* dbexchange will do something ... */
ospf6 = ospf6_get_by_lsdb(lsa);
+ assert(ospf6);
+
/* reinstall lsa */
ospf6_install_lsa(lsa);
@@ -994,6 +1000,30 @@ static char *ospf6_lsa_handler_name(const struct ospf6_lsa_handler *h)
return buf;
}
+DEFPY (debug_ospf6_lsa_aggregation,
+ debug_ospf6_lsa_aggregation_cmd,
+ "[no] debug ospf6 lsa aggregation",
+ NO_STR
+ DEBUG_STR
+ OSPF6_STR
+ "Debug Link State Advertisements (LSAs)\n"
+ "External LSA Aggregation\n")
+{
+
+ struct ospf6_lsa_handler *handler;
+
+ handler = ospf6_get_lsa_handler(OSPF6_LSTYPE_AS_EXTERNAL);
+ if (handler == NULL)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (no)
+ UNSET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_AGGR);
+ else
+ SET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_AGGR);
+
+ return CMD_SUCCESS;
+}
+
DEFUN (debug_ospf6_lsa_type,
debug_ospf6_lsa_hex_cmd,
"debug ospf6 lsa <router|network|inter-prefix|inter-router|as-external|link|intra-prefix|unknown> [<originate|examine|flooding>]",
@@ -1105,6 +1135,9 @@ void install_element_ospf6_debug_lsa(void)
install_element(ENABLE_NODE, &no_debug_ospf6_lsa_hex_cmd);
install_element(CONFIG_NODE, &debug_ospf6_lsa_hex_cmd);
install_element(CONFIG_NODE, &no_debug_ospf6_lsa_hex_cmd);
+
+ install_element(ENABLE_NODE, &debug_ospf6_lsa_aggregation_cmd);
+ install_element(CONFIG_NODE, &debug_ospf6_lsa_aggregation_cmd);
}
int config_write_ospf6_debug_lsa(struct vty *vty)
@@ -1128,6 +1161,8 @@ int config_write_ospf6_debug_lsa(struct vty *vty)
if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_FLOOD))
vty_out(vty, "debug ospf6 lsa %s flooding\n",
ospf6_lsa_handler_name(handler));
+ if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_AGGR))
+ vty_out(vty, "debug ospf6 lsa aggregation\n");
}
return 0;
diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h
index 15b0d4ebbc..4c95ee69bd 100644
--- a/ospf6d/ospf6_lsa.h
+++ b/ospf6d/ospf6_lsa.h
@@ -28,6 +28,7 @@
#define OSPF6_LSA_DEBUG_ORIGINATE 0x02
#define OSPF6_LSA_DEBUG_EXAMIN 0x04
#define OSPF6_LSA_DEBUG_FLOOD 0x08
+#define OSPF6_LSA_DEBUG_AGGR 0x10
/* OSPF LSA Default metric values */
#define DEFAULT_DEFAULT_METRIC 20
@@ -51,6 +52,8 @@
(ospf6_lstype_debug(type) & OSPF6_LSA_DEBUG_EXAMIN)
#define IS_OSPF6_DEBUG_FLOOD_TYPE(type) \
(ospf6_lstype_debug(type) & OSPF6_LSA_DEBUG_FLOOD)
+#define IS_OSPF6_DEBUG_AGGR \
+ (ospf6_lstype_debug(OSPF6_LSTYPE_AS_EXTERNAL) & OSPF6_LSA_DEBUG_AGGR) \
/* LSA definition */
@@ -263,4 +266,6 @@ extern void install_element_ospf6_debug_lsa(void);
extern void ospf6_lsa_age_set(struct ospf6_lsa *lsa);
extern void ospf6_flush_self_originated_lsas_now(struct ospf6 *ospf6);
extern struct ospf6 *ospf6_get_by_lsdb(struct ospf6_lsa *lsa);
+struct ospf6_lsa *ospf6_find_external_lsa(struct ospf6 *ospf6,
+ struct prefix *p);
#endif /* OSPF6_LSA_H */
diff --git a/ospf6d/ospf6_lsdb.c b/ospf6d/ospf6_lsdb.c
index 304f03fde8..039c65d739 100644
--- a/ospf6d/ospf6_lsdb.c
+++ b/ospf6d/ospf6_lsdb.c
@@ -30,6 +30,7 @@
#include "ospf6_proto.h"
#include "ospf6_lsa.h"
#include "ospf6_lsdb.h"
+#include "ospf6_asbr.h"
#include "ospf6_route.h"
#include "ospf6d.h"
#include "bitfield.h"
@@ -194,6 +195,28 @@ struct ospf6_lsa *ospf6_lsdb_lookup(uint16_t type, uint32_t id,
return (struct ospf6_lsa *)node->info;
}
+struct ospf6_lsa *ospf6_find_external_lsa(struct ospf6 *ospf6, struct prefix *p)
+{
+ struct ospf6_route *match;
+ struct ospf6_lsa *lsa;
+ struct ospf6_external_info *info;
+
+ match = ospf6_route_lookup(p, ospf6->external_table);
+ if (match == NULL) {
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug("No such route %pFX to withdraw", p);
+
+ return NULL;
+ }
+
+ info = match->route_option;
+ assert(info);
+
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ htonl(info->id), ospf6->router_id, ospf6->lsdb);
+ return lsa;
+}
+
struct ospf6_lsa *ospf6_lsdb_lookup_next(uint16_t type, uint32_t id,
uint32_t adv_router,
struct ospf6_lsdb *lsdb)
diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c
index 817e5372ea..549f5668b9 100644
--- a/ospf6d/ospf6_message.c
+++ b/ospf6d/ospf6_message.c
@@ -444,9 +444,9 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst,
/* check latency against hello period */
if (on->hello_in)
latency = monotime_since(&on->last_hello, NULL)
- - (oi->hello_interval * 1000000);
+ - ((int64_t)oi->hello_interval * 1000000);
/* log if latency exceeds the hello period */
- if (latency > (oi->hello_interval * 1000000))
+ if (latency > ((int64_t)oi->hello_interval * 1000000))
zlog_warn("%s RX %pI4 high latency %" PRId64 "us.", __func__,
&on->router_id, latency);
on->last_hello = timestamp;
@@ -1897,7 +1897,6 @@ static int ospf6_write(struct thread *thread)
struct ospf6_header *oh;
struct ospf6_packet *op;
struct listnode *node;
- char srcname[64], dstname[64];
struct iovec iovector[2];
int pkt_count = 0;
int len;
@@ -1934,48 +1933,62 @@ static int ospf6_write(struct thread *thread)
flog_err(EC_LIB_DEVELOPMENT,
"Could not send entire message");
- if (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND)) {
- inet_ntop(AF_INET6, &op->dst, dstname, sizeof(dstname));
- inet_ntop(AF_INET6, oi->linklocal_addr, srcname,
- sizeof(srcname));
+ if (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND_HDR)) {
zlog_debug("%s send on %s",
lookup_msg(ospf6_message_type_str, oh->type,
NULL),
oi->interface->name);
- zlog_debug(" src: %s", srcname);
- zlog_debug(" dst: %s", dstname);
+ zlog_debug(" src: %pI6", oi->linklocal_addr);
+ zlog_debug(" dst: %pI6", &op->dst);
+ switch (oh->type) {
+ case OSPF6_MESSAGE_TYPE_HELLO:
+ ospf6_hello_print(oh, OSPF6_ACTION_SEND);
+ break;
+ case OSPF6_MESSAGE_TYPE_DBDESC:
+ ospf6_dbdesc_print(oh, OSPF6_ACTION_SEND);
+ break;
+ case OSPF6_MESSAGE_TYPE_LSREQ:
+ ospf6_lsreq_print(oh, OSPF6_ACTION_SEND);
+ break;
+ case OSPF6_MESSAGE_TYPE_LSUPDATE:
+ ospf6_lsupdate_print(oh, OSPF6_ACTION_SEND);
+ break;
+ case OSPF6_MESSAGE_TYPE_LSACK:
+ ospf6_lsack_print(oh, OSPF6_ACTION_SEND);
+ break;
+ default:
+ zlog_debug("Unknown message");
+ assert(0);
+ break;
+ }
}
switch (oh->type) {
case OSPF6_MESSAGE_TYPE_HELLO:
monotime(&timestamp);
if (oi->hello_out)
latency = monotime_since(&oi->last_hello, NULL)
- - (oi->hello_interval * 1000000);
+ - ((int64_t)oi->hello_interval
+ * 1000000);
/* log if latency exceeds the hello period */
- if (latency > (oi->hello_interval * 1000000))
+ if (latency > ((int64_t)oi->hello_interval * 1000000))
zlog_warn("%s hello TX high latency %" PRId64
"us.",
__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++;
- ospf6_dbdesc_print(oh, OSPF6_ACTION_SEND);
break;
case OSPF6_MESSAGE_TYPE_LSREQ:
oi->ls_req_out++;
- ospf6_lsreq_print(oh, OSPF6_ACTION_SEND);
break;
case OSPF6_MESSAGE_TYPE_LSUPDATE:
oi->ls_upd_out++;
- ospf6_lsupdate_print(oh, OSPF6_ACTION_SEND);
break;
case OSPF6_MESSAGE_TYPE_LSACK:
oi->ls_ack_out++;
- ospf6_lsack_print(oh, OSPF6_ACTION_SEND);
break;
default:
zlog_debug("Unknown message");
@@ -2356,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..470a5b1338 100644
--- a/ospf6d/ospf6_nssa.c
+++ b/ospf6d/ospf6_nssa.c
@@ -1159,10 +1159,49 @@ static void ospf6_nssa_flush_area(struct ospf6_area *area)
}
}
-static void ospf6_area_nssa_update(struct ospf6_area *area)
+static void ospf6_check_and_originate_type7_lsa(struct ospf6_area *area)
{
struct ospf6_route *route;
+ struct route_node *rn = NULL;
+ struct ospf6_external_aggr_rt *aggr;
+
+ /* Loop through the external_table to find the LSAs originated
+ * without aggregation and originate type-7 LSAs for them.
+ */
+ for (route = ospf6_route_head(
+ area->ospf6->external_table);
+ route; route = ospf6_route_next(route)) {
+ /* This means the Type-5 LSA was originated for this route */
+ if (route->path.origin.id != 0)
+ ospf6_nssa_lsa_originate(route, area);
+
+ }
+
+ /* Loop through the aggregation table to originate type-7 LSAs
+ * for the aggregated type-5 LSAs
+ */
+ for (rn = route_top(area->ospf6->rt_aggr_tbl); rn;
+ rn = route_next(rn)) {
+ if (!rn->info)
+ continue;
+
+ aggr = rn->info;
+
+ if (CHECK_FLAG(aggr->aggrflags,
+ OSPF6_EXTERNAL_AGGRT_ORIGINATED)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "Originating Type-7 LSAs for area %s",
+ area->name);
+
+ ospf6_nssa_lsa_originate(aggr->route, area);
+ }
+ }
+}
+
+static void ospf6_area_nssa_update(struct ospf6_area *area)
+{
if (IS_AREA_NSSA(area)) {
if (!ospf6_check_and_set_router_abr(area->ospf6))
OSPF6_OPT_CLEAR(area->options, OSPF6_OPT_E);
@@ -1194,10 +1233,7 @@ static void ospf6_area_nssa_update(struct ospf6_area *area)
zlog_debug("NSSA area %s", area->name);
/* Originate NSSA LSA */
- for (route = ospf6_route_head(
- area->ospf6->external_table);
- route; route = ospf6_route_next(route))
- ospf6_nssa_lsa_originate(route, area);
+ ospf6_check_and_originate_type7_lsa(area);
}
} else {
/* Disable NSSA */
@@ -1259,13 +1295,10 @@ void ospf6_nssa_lsa_originate(struct ospf6_route *route,
struct in6_addr *fwd_addr;
struct ospf6_as_external_lsa *as_external_lsa;
- char buf[PREFIX2STR_BUFFER];
caddr_t p;
- if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE(AS_EXTERNAL)) {
- prefix2str(&route->prefix, buf, sizeof(buf));
- zlog_debug("Originate AS-External-LSA for %s", buf);
- }
+ if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE(AS_EXTERNAL))
+ zlog_debug("Originate NSSA-LSA for %pFX", &route->prefix);
/* prepare buffer */
memset(buffer, 0, sizeof(buffer));
@@ -1296,7 +1329,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;
@@ -1334,7 +1367,7 @@ void ospf6_nssa_lsa_originate(struct ospf6_route *route,
lsa_header->adv_router = area->ospf6->router_id;
lsa_header->seqnum =
ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id,
- lsa_header->adv_router, area->ospf6->lsdb);
+ lsa_header->adv_router, area->lsdb);
lsa_header->length = htons((caddr_t)p - (caddr_t)lsa_header);
/* LSA checksum */
diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c
index 0a026785f4..cd3139d28a 100644
--- a/ospf6d/ospf6_route.c
+++ b/ospf6d/ospf6_route.c
@@ -284,12 +284,21 @@ void ospf6_add_nexthop(struct list *nh_list, int ifindex, struct in6_addr *addr)
struct ospf6_nexthop nh_match;
if (nh_list) {
- nh_match.ifindex = ifindex;
- if (addr != NULL)
+ if (addr) {
+ if (ifindex)
+ nh_match.type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ else
+ nh_match.type = NEXTHOP_TYPE_IPV6;
+
memcpy(&nh_match.address, addr,
sizeof(struct in6_addr));
- else
+ } else {
+ nh_match.type = NEXTHOP_TYPE_IFINDEX;
+
memset(&nh_match.address, 0, sizeof(struct in6_addr));
+ }
+
+ nh_match.ifindex = ifindex;
if (!ospf6_route_find_nexthop(nh_list, &nh_match)) {
nh = ospf6_nexthop_create();
@@ -299,36 +308,76 @@ void ospf6_add_nexthop(struct list *nh_list, int ifindex, struct in6_addr *addr)
}
}
+void ospf6_add_route_nexthop_blackhole(struct ospf6_route *route)
+{
+ struct ospf6_nexthop *nh;
+ struct ospf6_nexthop nh_match = {};
+
+ /* List not allocated. */
+ if (route->nh_list == NULL)
+ return;
+
+ /* Entry already exists. */
+ nh_match.type = NEXTHOP_TYPE_BLACKHOLE;
+ if (ospf6_route_find_nexthop(route->nh_list, &nh_match))
+ return;
+
+ nh = ospf6_nexthop_create();
+ ospf6_nexthop_copy(nh, &nh_match);
+ listnode_add(route->nh_list, nh);
+}
+
void ospf6_route_zebra_copy_nexthops(struct ospf6_route *route,
struct zapi_nexthop nexthops[],
int entries, vrf_id_t vrf_id)
{
struct ospf6_nexthop *nh;
struct listnode *node;
- char buf[64];
int i;
if (route) {
i = 0;
for (ALL_LIST_ELEMENTS_RO(route->nh_list, node, nh)) {
if (IS_OSPF6_DEBUG_ZEBRA(SEND)) {
- const char *ifname;
- inet_ntop(AF_INET6, &nh->address, buf,
- sizeof(buf));
- ifname = ifindex2ifname(nh->ifindex, vrf_id);
- zlog_debug(" nexthop: %s%%%.*s(%d)", buf,
- IFNAMSIZ, ifname, nh->ifindex);
+ zlog_debug(" nexthop: %s %pI6%%%.*s(%d)",
+ nexthop_type_to_str(nh->type),
+ &nh->address, IFNAMSIZ,
+ ifindex2ifname(nh->ifindex, vrf_id),
+ nh->ifindex);
}
+
if (i >= entries)
return;
nexthops[i].vrf_id = vrf_id;
- nexthops[i].ifindex = nh->ifindex;
- if (!IN6_IS_ADDR_UNSPECIFIED(&nh->address)) {
+ nexthops[i].type = nh->type;
+
+ switch (nh->type) {
+ case NEXTHOP_TYPE_BLACKHOLE:
+ /* NOTHING */
+ break;
+
+ case NEXTHOP_TYPE_IFINDEX:
+ nexthops[i].ifindex = nh->ifindex;
+ break;
+
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ case NEXTHOP_TYPE_IPV4:
+ /*
+ * OSPFv3 with IPv4 routes is not supported
+ * yet. Skip this next hop.
+ */
+ if (IS_OSPF6_DEBUG_ZEBRA(SEND))
+ zlog_debug(" Skipping IPv4 next hop");
+ continue;
+
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ nexthops[i].ifindex = nh->ifindex;
+ /* FALLTHROUGH */
+ case NEXTHOP_TYPE_IPV6:
nexthops[i].gate.ipv6 = nh->address;
- nexthops[i].type = NEXTHOP_TYPE_IPV6_IFINDEX;
- } else
- nexthops[i].type = NEXTHOP_TYPE_IFINDEX;
+ break;
+ }
i++;
}
}
@@ -404,7 +453,7 @@ void ospf6_copy_paths(struct list *dst, struct list *src)
}
}
-struct ospf6_route *ospf6_route_create(void)
+struct ospf6_route *ospf6_route_create(struct ospf6 *ospf6)
{
struct ospf6_route *route;
@@ -415,6 +464,8 @@ struct ospf6_route *ospf6_route_create(void)
route->paths = list_new();
route->paths->cmp = (int (*)(void *, void *))ospf6_path_cmp;
route->paths->del = (void (*)(void *))ospf6_path_free;
+ route->ospf6 = ospf6;
+
return route;
}
@@ -433,9 +484,10 @@ struct ospf6_route *ospf6_route_copy(struct ospf6_route *route)
{
struct ospf6_route *new;
- new = ospf6_route_create();
+ new = ospf6_route_create(route->ospf6);
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 +1189,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 +1317,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..991720ec2e 100644
--- a/ospf6d/ospf6_route.h
+++ b/ospf6d/ospf6_route.h
@@ -24,6 +24,7 @@
#include "command.h"
#include "zclient.h"
#include "lib/json.h"
+#include "lib/nexthop.h"
#define OSPF6_MULTI_PATH_LIMIT 4
@@ -44,23 +45,60 @@ struct ospf6_nexthop {
/* IP address, if any */
struct in6_addr address;
+
+ /** Next-hop type information. */
+ enum nexthop_types_t type;
};
-#define ospf6_nexthop_is_set(x) \
- ((x)->ifindex || !IN6_IS_ADDR_UNSPECIFIED(&(x)->address))
-#define ospf6_nexthop_is_same(a, b) \
- ((a)->ifindex == (b)->ifindex \
- && IN6_ARE_ADDR_EQUAL(&(a)->address, &(b)->address))
-#define ospf6_nexthop_clear(x) \
- do { \
- (x)->ifindex = 0; \
- memset(&(x)->address, 0, sizeof(struct in6_addr)); \
- } while (0)
-#define ospf6_nexthop_copy(a, b) \
- do { \
- (a)->ifindex = (b)->ifindex; \
- memcpy(&(a)->address, &(b)->address, sizeof(struct in6_addr)); \
- } while (0)
+static inline bool ospf6_nexthop_is_set(const struct ospf6_nexthop *nh)
+{
+ return nh->type != 0;
+}
+
+static inline bool ospf6_nexthop_is_same(const struct ospf6_nexthop *nha,
+ const struct ospf6_nexthop *nhb)
+{
+ if (nha->type != nhb->type)
+ return false;
+
+ switch (nha->type) {
+ case NEXTHOP_TYPE_BLACKHOLE:
+ /* NOTHING */
+ break;
+
+ case NEXTHOP_TYPE_IFINDEX:
+ if (nha->ifindex != nhb->ifindex)
+ return false;
+ break;
+
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ case NEXTHOP_TYPE_IPV4:
+ /* OSPFv3 does not support IPv4 next hops. */
+ return false;
+
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ if (nha->ifindex != nhb->ifindex)
+ return false;
+ /* FALLTHROUGH */
+ case NEXTHOP_TYPE_IPV6:
+ if (!IN6_ARE_ADDR_EQUAL(&nha->address, &nhb->address))
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+static inline void ospf6_nexthop_clear(struct ospf6_nexthop *nh)
+{
+ memset(nh, 0, sizeof(*nh));
+}
+
+static inline void ospf6_nexthop_copy(struct ospf6_nexthop *nha,
+ const struct ospf6_nexthop *nhb)
+{
+ memcpy(nha, nhb, sizeof(*nha));
+}
/* Path */
struct ospf6_ls_origin {
@@ -79,9 +117,6 @@ struct ospf6_path {
/* Optional Capabilities */
uint8_t options[3];
- /* Prefix Options */
- uint8_t prefix_options;
-
/* Associated Area */
in_addr_t area_id;
@@ -127,6 +162,9 @@ struct ospf6_route {
struct ospf6_route *prev;
struct ospf6_route *next;
+ /* Back pointer to ospf6 */
+ struct ospf6 *ospf6;
+
unsigned int lock;
/* Destination Type */
@@ -147,6 +185,9 @@ struct ospf6_route {
/* flag */
uint8_t flag;
+ /* Prefix Options */
+ uint8_t prefix_options;
+
/* route option */
void *route_option;
@@ -161,6 +202,12 @@ struct ospf6_route {
/* nexthop */
struct list *nh_list;
+
+ /* points to the summarised route */
+ struct ospf6_external_aggr_rt *aggr_route;
+
+ /* For Aggr routes */
+ bool to_be_processed;
};
#define OSPF6_DEST_TYPE_NONE 0
@@ -279,6 +326,7 @@ extern void ospf6_copy_nexthops(struct list *dst, struct list *src);
extern void ospf6_merge_nexthops(struct list *dst, struct list *src);
extern void ospf6_add_nexthop(struct list *nh_list, int ifindex,
struct in6_addr *addr);
+extern void ospf6_add_route_nexthop_blackhole(struct ospf6_route *route);
extern int ospf6_num_nexthops(struct list *nh_list);
extern int ospf6_route_cmp_nexthops(struct ospf6_route *a,
struct ospf6_route *b);
@@ -294,7 +342,7 @@ extern int ospf6_route_get_first_nh_index(struct ospf6_route *route);
#define ospf6_route_add_nexthop(route, ifindex, addr) \
ospf6_add_nexthop(route->nh_list, ifindex, addr)
-extern struct ospf6_route *ospf6_route_create(void);
+extern struct ospf6_route *ospf6_route_create(struct ospf6 *ospf6);
extern void ospf6_route_delete(struct ospf6_route *);
extern struct ospf6_route *ospf6_route_copy(struct ospf6_route *route);
extern int ospf6_route_cmp(struct ospf6_route *ra, struct ospf6_route *rb);
diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c
index 032484e288..4e7a7146eb 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)
@@ -370,7 +374,7 @@ static int ospf6_spf_install(struct ospf6_vertex *v,
up to here. */
assert(route == NULL);
- route = ospf6_route_create();
+ route = ospf6_route_create(v->area->ospf6);
memcpy(&route->prefix, &v->vertex_id, sizeof(struct prefix));
route->type = OSPF6_DEST_TYPE_LINKSTATE;
route->path.type = OSPF6_PATH_TYPE_INTRA;
diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c
index 6f40989efd..6105e2c24b 100644
--- a/ospf6d/ospf6_top.c
+++ b/ospf6d/ospf6_top.c
@@ -409,13 +409,31 @@ static struct ospf6 *ospf6_create(const char *name)
o->external_table = OSPF6_ROUTE_TABLE_CREATE(GLOBAL, EXTERNAL_ROUTES);
o->external_table->scope = o;
-
+ /* Setting this to 1, so that the LS ID 0 can be considered as invalid
+ * for self originated external LSAs. This helps in differentiating if
+ * an LSA is originated for any route or not in the route data.
+ * rt->route_option->id is by default 0
+ * Consider a route having id as 0 and prefix as 1::1, an external LSA
+ * is originated with ID 0.0.0.0. Now consider another route 2::2
+ * and for this LSA was not originated because of some configuration
+ * but the ID field rt->route_option->id is still 0.Consider now this
+ * 2::2 is being deleted, it will search LSA with LS ID as 0 and it
+ * will find the LSA and hence delete it but the LSA belonged to prefix
+ * 1::1, this happened because of LS ID 0.
+ */
+ o->external_id = OSPF6_EXT_INIT_LS_ID;
o->external_id_table = route_table_init();
o->write_oi_count = OSPF6_WRITE_INTERFACE_COUNT_DEFAULT;
o->ref_bandwidth = OSPF6_REFERENCE_BANDWIDTH;
o->distance_table = route_table_init();
+
+ o->rt_aggr_tbl = route_table_init();
+ o->aggr_delay_interval = OSPF6_EXTL_AGGR_DEFAULT_DELAY;
+ o->t_external_aggr = NULL;
+ o->aggr_action = OSPF6_ROUTE_AGGR_NONE;
+
o->fd = -1;
o->max_multipath = MULTIPATH_NUM;
@@ -461,6 +479,7 @@ struct ospf6 *ospf6_instance_create(const char *name)
void ospf6_delete(struct ospf6 *o)
{
struct listnode *node, *nnode;
+ struct route_node *rn = NULL;
struct ospf6_area *oa;
struct vrf *vrf;
@@ -499,6 +518,11 @@ void ospf6_delete(struct ospf6 *o)
ospf6_vrf_unlink(o, vrf);
}
+ for (rn = route_top(o->rt_aggr_tbl); rn; rn = route_next(rn))
+ if (rn->info)
+ ospf6_external_aggregator_free(rn->info);
+ route_table_finish(o->rt_aggr_tbl);
+
XFREE(MTYPE_OSPF6_TOP, o->name);
XFREE(MTYPE_OSPF6_TOP, o);
}
@@ -527,6 +551,7 @@ static void ospf6_disable(struct ospf6 *o)
THREAD_OFF(o->t_ase_calc);
THREAD_OFF(o->t_distribute_update);
THREAD_OFF(o->t_ospf6_receive);
+ THREAD_OFF(o->t_external_aggr);
}
}
@@ -690,6 +715,7 @@ static void ospf6_process_reset(struct ospf6 *ospf6)
struct interface *ifp;
struct vrf *vrf = vrf_lookup_by_id(ospf6->vrf_id);
+ ospf6_unset_all_aggr_flag(ospf6);
ospf6_flush_self_originated_lsas_now(ospf6);
ospf6->inst_shutdown = 0;
ospf6_db_clear(ospf6);
@@ -989,7 +1015,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 +1037,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;
@@ -1672,6 +1680,424 @@ DEFUN(show_ipv6_ospf6_route_type_detail, show_ipv6_ospf6_route_type_detail_cmd,
return CMD_SUCCESS;
}
+bool ospf6_is_valid_summary_addr(struct vty *vty, struct prefix *p)
+{
+ struct in6_addr addr_zero;
+
+ memset(&addr_zero, 0, sizeof(struct in6_addr));
+
+ /* Default prefix validation*/
+ if ((is_default_prefix((struct prefix *)p))
+ || (!memcmp(&p->u.prefix6, &addr_zero, sizeof(struct in6_addr)))) {
+ vty_out(vty, "Default address should not be configured as summary address.\n");
+ return false;
+ }
+
+ /* Host route should not be configured as summary address */
+ if (p->prefixlen == IPV6_MAX_BITLEN) {
+ vty_out(vty, "Host route should not be configured as summary address.\n");
+ return false;
+ }
+
+ return true;
+}
+
+/* External Route Aggregation */
+DEFPY (ospf6_external_route_aggregation,
+ ospf6_external_route_aggregation_cmd,
+ "summary-address X:X::X:X/M$prefix [tag (1-4294967295)] [{metric (0-16777215) | metric-type (1-2)$mtype}]",
+ "External summary address\n"
+ "Specify IPv6 prefix\n"
+ "Router tag \n"
+ "Router tag value\n"
+ "Metric \n"
+ "Advertised metric for this route\n"
+ "OSPFv3 exterior metric type for summarised routes\n"
+ "Set OSPFv3 External Type 1/2 metrics\n")
+{
+ VTY_DECLVAR_CONTEXT(ospf6, ospf6);
+
+ struct prefix p;
+ int ret = CMD_SUCCESS;
+
+ p.family = AF_INET6;
+ ret = str2prefix(prefix_str, &p);
+ if (ret == 0) {
+ vty_out(vty, "Malformed prefix\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Apply mask for given prefix. */
+ apply_mask((struct prefix *)&p);
+
+ if (!ospf6_is_valid_summary_addr(vty, &p))
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (!tag_str)
+ tag = 0;
+
+ if (!metric_str)
+ metric = -1;
+
+ if (!mtype_str)
+ mtype = DEFAULT_METRIC_TYPE;
+
+ ret = ospf6_external_aggr_config_set(ospf6, &p, tag, metric, mtype);
+ if (ret == OSPF6_FAILURE) {
+ vty_out(vty, "Invalid configuration!!\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(no_ospf6_external_route_aggregation,
+ no_ospf6_external_route_aggregation_cmd,
+ "no summary-address X:X::X:X/M$prefix [tag (1-4294967295)] [{metric (0-16777215) | metric-type (1-2)}]",
+ NO_STR
+ "External summary address\n"
+ "Specify IPv6 prefix\n"
+ "Router tag\n"
+ "Router tag value\n"
+ "Metric \n"
+ "Advertised metric for this route\n"
+ "OSPFv3 exterior metric type for summarised routes\n"
+ "Set OSPFv3 External Type 1/2 metrics\n")
+{
+ VTY_DECLVAR_CONTEXT(ospf6, ospf6);
+
+ struct prefix p;
+ int ret = CMD_SUCCESS;
+
+ ret = str2prefix(prefix_str, &p);
+ if (ret == 0) {
+ vty_out(vty, "Malformed prefix\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Apply mask for given prefix. */
+ apply_mask((struct prefix *)&p);
+
+ if (!ospf6_is_valid_summary_addr(vty, &p))
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = ospf6_external_aggr_config_unset(ospf6, &p);
+ if (ret == OSPF6_INVALID)
+ vty_out(vty, "Invalid configuration!!\n");
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (ospf6_external_route_aggregation_no_advertise,
+ ospf6_external_route_aggregation_no_advertise_cmd,
+ "summary-address X:X::X:X/M$prefix no-advertise",
+ "External summary address\n"
+ "Specify IPv6 prefix\n"
+ "Don't advertise summary route \n")
+{
+ VTY_DECLVAR_CONTEXT(ospf6, ospf6);
+
+ struct prefix p;
+ int ret = CMD_SUCCESS;
+
+ ret = str2prefix(prefix_str, &p);
+ if (ret == 0) {
+ vty_out(vty, "Malformed prefix\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Apply mask for given prefix. */
+ apply_mask((struct prefix *)&p);
+
+ if (!ospf6_is_valid_summary_addr(vty, &p))
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = ospf6_asbr_external_rt_no_advertise(ospf6, &p);
+ if (ret == OSPF6_INVALID)
+ vty_out(vty, "!!Invalid configuration\n");
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (no_ospf6_external_route_aggregation_no_advertise,
+ no_ospf6_external_route_aggregation_no_advertise_cmd,
+ "no summary-address X:X::X:X/M$prefix no-advertise",
+ NO_STR
+ "External summary address\n"
+ "Specify IPv6 prefix\n"
+ "Adverise summary route to the AS \n")
+{
+ VTY_DECLVAR_CONTEXT(ospf6, ospf6);
+
+ struct prefix p;
+ int ret = CMD_SUCCESS;
+
+ ret = str2prefix(prefix_str, &p);
+ if (ret == 0) {
+ vty_out(vty, "Malformed prefix\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Apply mask for given prefix. */
+ apply_mask((struct prefix *)&p);
+
+ if (!ospf6_is_valid_summary_addr(vty, &p))
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = ospf6_asbr_external_rt_advertise(ospf6, &p);
+ if (ret == OSPF6_INVALID)
+ vty_out(vty, "!!Invalid configuration\n");
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (ospf6_route_aggregation_timer,
+ ospf6_route_aggregation_timer_cmd,
+ "aggregation timer (5-1800)",
+ "External route aggregation\n"
+ "Delay timer (in seconds)\n"
+ "Timer interval(in seconds)\n")
+{
+ VTY_DECLVAR_CONTEXT(ospf6, ospf6);
+
+ ospf6_external_aggr_delay_timer_set(ospf6, timer);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (no_ospf6_route_aggregation_timer,
+ no_ospf6_route_aggregation_timer_cmd,
+ "no aggregation timer [5-1800]",
+ NO_STR
+ "External route aggregation\n"
+ "Delay timer\n"
+ "Timer interval(in seconds)\n")
+{
+ VTY_DECLVAR_CONTEXT(ospf6, ospf6);
+
+ ospf6_external_aggr_delay_timer_set(ospf6,
+ OSPF6_EXTL_AGGR_DEFAULT_DELAY);
+ return CMD_SUCCESS;
+}
+
+static int
+ospf6_print_vty_external_routes_walkcb(struct hash_bucket *bucket, void *arg)
+{
+ struct ospf6_route *rt = bucket->data;
+ struct vty *vty = (struct vty *)arg;
+ static unsigned int count;
+
+ vty_out(vty, "%pFX ", &rt->prefix);
+
+ count++;
+
+ if (count%5 == 0)
+ vty_out(vty, "\n");
+
+ if (OSPF6_EXTERNAL_RT_COUNT(rt->aggr_route) == count)
+ count = 0;
+
+ return HASHWALK_CONTINUE;
+}
+
+static int
+ospf6_print_json_external_routes_walkcb(struct hash_bucket *bucket, void *arg)
+{
+ struct ospf6_route *rt = bucket->data;
+ struct json_object *json = (struct json_object *)arg;
+ char buf[PREFIX2STR_BUFFER];
+ char exnalbuf[20];
+ static unsigned int count;
+
+ prefix2str(&rt->prefix, buf, sizeof(buf));
+
+ snprintf(exnalbuf, sizeof(exnalbuf), "Exnl Addr-%d", count);
+
+ json_object_string_add(json, exnalbuf, buf);
+
+ count++;
+
+ if (OSPF6_EXTERNAL_RT_COUNT(rt->aggr_route) == count)
+ count = 0;
+
+ return HASHWALK_CONTINUE;
+}
+
+static void
+ospf6_show_vrf_name(struct vty *vty, struct ospf6 *ospf6,
+ json_object *json)
+{
+ if (json) {
+ if (ospf6->vrf_id == VRF_DEFAULT)
+ json_object_string_add(json, "vrfName",
+ "default");
+ else
+ json_object_string_add(json, "vrfName",
+ ospf6->name);
+ json_object_int_add(json, "vrfId", ospf6->vrf_id);
+ } else {
+ if (ospf6->vrf_id == VRF_DEFAULT)
+ vty_out(vty, "VRF Name: %s\n", "default");
+ else if (ospf6->name)
+ vty_out(vty, "VRF Name: %s\n", ospf6->name);
+ }
+}
+
+static int
+ospf6_show_summary_address(struct vty *vty, struct ospf6 *ospf6,
+ json_object *json,
+ bool uj, const char *detail)
+{
+ struct route_node *rn;
+ static const char header[] = "Summary-address Metric-type Metric Tag External_Rt_count\n";
+ json_object *json_vrf = NULL;
+
+ if (!uj) {
+ ospf6_show_vrf_name(vty, ospf6, json_vrf);
+ vty_out(vty, "aggregation delay interval :%d(in seconds)\n\n",
+ ospf6->aggr_delay_interval);
+ vty_out(vty, "%s\n", header);
+ } else {
+ json_vrf = json_object_new_object();
+
+ ospf6_show_vrf_name(vty, ospf6, json_vrf);
+
+ json_object_int_add(json_vrf, "aggregation delay interval",
+ ospf6->aggr_delay_interval);
+ }
+
+
+ for (rn = route_top(ospf6->rt_aggr_tbl); rn; rn = route_next(rn)) {
+ if (!rn->info)
+ continue;
+
+ struct ospf6_external_aggr_rt *aggr = rn->info;
+ json_object *json_aggr = NULL;
+ char buf[PREFIX2STR_BUFFER];
+
+ prefix2str(&aggr->p, buf, sizeof(buf));
+
+ if (uj) {
+
+ json_aggr = json_object_new_object();
+
+ json_object_object_add(json_vrf,
+ buf,
+ json_aggr);
+
+ json_object_string_add(json_aggr,
+ "Summary address",
+ buf);
+
+ json_object_string_add(
+ json_aggr, "Metric-type",
+ (aggr->mtype == DEFAULT_METRIC_TYPE)
+ ? "E2"
+ : "E1");
+
+ json_object_int_add(json_aggr, "Metric",
+ (aggr->metric != -1)
+ ? aggr->metric
+ : DEFAULT_DEFAULT_METRIC);
+
+ json_object_int_add(json_aggr, "Tag",
+ aggr->tag);
+
+ json_object_int_add(json_aggr,
+ "External route count",
+ OSPF6_EXTERNAL_RT_COUNT(aggr));
+
+ if (OSPF6_EXTERNAL_RT_COUNT(aggr) && detail) {
+ json_object_int_add(json_aggr, "ID",
+ aggr->id);
+ json_object_int_add(json_aggr, "Flags",
+ aggr->aggrflags);
+ hash_walk(aggr->match_extnl_hash,
+ ospf6_print_json_external_routes_walkcb,
+ json_aggr);
+ }
+
+ } else {
+ vty_out(vty, "%-22s", buf);
+
+ (aggr->mtype == DEFAULT_METRIC_TYPE)
+ ? vty_out(vty, "%-16s", "E2")
+ : vty_out(vty, "%-16s", "E1");
+ vty_out(vty, "%-11d", (aggr->metric != -1)
+ ? aggr->metric
+ : DEFAULT_DEFAULT_METRIC);
+
+ vty_out(vty, "%-12u", aggr->tag);
+
+ vty_out(vty, "%-5ld\n",
+ OSPF6_EXTERNAL_RT_COUNT(aggr));
+
+ if (OSPF6_EXTERNAL_RT_COUNT(aggr) && detail) {
+ vty_out(vty,
+ "Matched External routes:\n");
+ hash_walk(aggr->match_extnl_hash,
+ ospf6_print_vty_external_routes_walkcb,
+ vty);
+ vty_out(vty, "\n");
+ }
+
+ vty_out(vty, "\n");
+ }
+ }
+
+ if (uj)
+ json_object_object_add(json, ospf6->name,
+ json_vrf);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_ipv6_ospf6_external_aggregator,
+ show_ipv6_ospf6_external_aggregator_cmd,
+ "show ipv6 ospf6 [vrf <NAME|all>] summary-address [detail$detail] [json]",
+ SHOW_STR
+ IP6_STR
+ OSPF6_STR
+ VRF_CMD_HELP_STR
+ "All VRFs\n"
+ "Show external summary addresses\n"
+ "detailed informtion\n"
+ JSON_STR)
+{
+ bool uj = use_json(argc, argv);
+ struct ospf6 *ospf6 = NULL;
+ json_object *json = NULL;
+ const char *vrf_name = NULL;
+ struct listnode *node;
+ bool all_vrf = false;
+ int idx_vrf = 0;
+
+ if (uj)
+ json = json_object_new_object();
+
+ OSPF6_CMD_CHECK_RUNNING();
+ OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
+
+ for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) {
+ if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) {
+
+ ospf6_show_summary_address(vty, ospf6, json, uj,
+ detail);
+
+ if (!all_vrf)
+ break;
+ }
+ }
+
+ if (uj) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+
+ return CMD_SUCCESS;
+}
+
static void ospf6_stub_router_config_write(struct vty *vty, struct ospf6 *ospf6)
{
if (CHECK_FLAG(ospf6->flag, OSPF6_STUB_ROUTER)) {
@@ -1711,6 +2137,44 @@ static int ospf6_distance_config_write(struct vty *vty, struct ospf6 *ospf6)
return 0;
}
+static int ospf6_asbr_summary_config_write(struct vty *vty, struct ospf6 *ospf6)
+{
+ struct route_node *rn;
+ struct ospf6_external_aggr_rt *aggr;
+ char buf[PREFIX2STR_BUFFER];
+
+ if (ospf6->aggr_delay_interval != OSPF6_EXTL_AGGR_DEFAULT_DELAY)
+ vty_out(vty, " aggregation timer %u\n",
+ ospf6->aggr_delay_interval);
+
+ /* print 'summary-address A:B::C:D/M' */
+ for (rn = route_top(ospf6->rt_aggr_tbl); rn; rn = route_next(rn)) {
+ if (!rn->info)
+ continue;
+
+ aggr = rn->info;
+
+ prefix2str(&aggr->p, buf, sizeof(buf));
+ vty_out(vty, " summary-address %s", buf);
+ if (aggr->tag)
+ vty_out(vty, " tag %u", aggr->tag);
+
+ if (aggr->metric != -1)
+ vty_out(vty, " metric %d", aggr->metric);
+
+ if (aggr->mtype != DEFAULT_METRIC_TYPE)
+ vty_out(vty, " metric-type %d", aggr->mtype);
+
+ if (CHECK_FLAG(aggr->aggrflags,
+ OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE))
+ vty_out(vty, " no-advertise");
+
+ vty_out(vty, "\n");
+ }
+
+ return 0;
+}
+
/* OSPF configuration write function. */
static int config_write_ospf6(struct vty *vty)
{
@@ -1768,6 +2232,7 @@ static int config_write_ospf6(struct vty *vty)
ospf6_spf_config_write(vty, ospf6);
ospf6_distance_config_write(vty, ospf6);
ospf6_distribute_config_write(vty, ospf6);
+ ospf6_asbr_summary_config_write(vty, ospf6);
vty_out(vty, "!\n");
}
@@ -1826,6 +2291,17 @@ void ospf6_top_init(void)
install_element(OSPF6_NODE, &ospf6_max_multipath_cmd);
install_element(OSPF6_NODE, &no_ospf6_max_multipath_cmd);
+ /* ASBR Summarisation */
+ install_element(OSPF6_NODE, &ospf6_external_route_aggregation_cmd);
+ install_element(OSPF6_NODE, &no_ospf6_external_route_aggregation_cmd);
+ install_element(OSPF6_NODE,
+ &ospf6_external_route_aggregation_no_advertise_cmd);
+ install_element(OSPF6_NODE,
+ &no_ospf6_external_route_aggregation_no_advertise_cmd);
+ install_element(OSPF6_NODE, &ospf6_route_aggregation_timer_cmd);
+ install_element(OSPF6_NODE, &no_ospf6_route_aggregation_timer_cmd);
+ install_element(VIEW_NODE, &show_ipv6_ospf6_external_aggregator_cmd);
+
install_element(OSPF6_NODE, &ospf6_distance_cmd);
install_element(OSPF6_NODE, &no_ospf6_distance_cmd);
install_element(OSPF6_NODE, &ospf6_distance_ospf6_cmd);
diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h
index 3eb423f681..fe02cd3f84 100644
--- a/ospf6d/ospf6_top.h
+++ b/ospf6d/ospf6_top.h
@@ -91,6 +91,7 @@ struct ospf6 {
struct ospf6_route_table *external_table;
struct route_table *external_id_table;
+#define OSPF6_EXT_INIT_LS_ID 1
uint32_t external_id;
/* OSPF6 redistribute configuration */
@@ -130,6 +131,7 @@ struct ospf6 {
struct thread *maxage_remover;
struct thread *t_distribute_update; /* Distirbute update timer. */
struct thread *t_ospf6_receive; /* OSPF6 receive timer */
+ struct thread *t_external_aggr; /* OSPF6 aggregation timer */
#define OSPF6_WRITE_INTERFACE_COUNT_DEFAULT 20
struct thread *t_write;
@@ -158,16 +160,22 @@ struct ospf6 {
struct list *oi_write_q;
uint32_t redist_count;
+
+ /* Action for aggregation of external LSAs */
+ int aggr_action;
+
+#define OSPF6_EXTL_AGGR_DEFAULT_DELAY 5
+ /* For ASBR summary delay timer */
+ int aggr_delay_interval;
+ /* Table of configured Aggregate addresses */
+ struct route_table *rt_aggr_tbl;
+
QOBJ_FIELDS;
};
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;
@@ -188,4 +196,5 @@ struct ospf6 *ospf6_lookup_by_vrf_id(vrf_id_t vrf_id);
struct ospf6 *ospf6_lookup_by_vrf_name(const char *name);
const char *ospf6_vrf_id_to_name(vrf_id_t vrf_id);
void ospf6_vrf_init(void);
+bool ospf6_is_valid_summary_addr(struct vty *vty, struct prefix *p);
#endif /* OSPF6_TOP_H */
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/ospf6d.h b/ospf6d/ospf6d.h
index e054803df3..5afece9b0a 100644
--- a/ospf6d/ospf6d.h
+++ b/ospf6d/ospf6d.h
@@ -49,6 +49,10 @@ extern struct thread_master *master;
#define MSG_OK 0
#define MSG_NG 1
+#define OSPF6_SUCCESS 1
+#define OSPF6_FAILURE 0
+#define OSPF6_INVALID -1
+
/* cast macro: XXX - these *must* die, ick ick. */
#define OSPF6_PROCESS(x) ((struct ospf6 *) (x))
#define OSPF6_AREA(x) ((struct ospf6_area *) (x))
diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am
index 2b7bce5392..78fb26b00e 100644
--- a/ospf6d/subdir.am
+++ b/ospf6d/subdir.am
@@ -84,12 +84,13 @@ 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 += \
ospf6d/ospf6_top.c \
ospf6d/ospf6_asbr.c \
+ ospf6d/ospf6_lsa.c \
# end
nodist_ospf6d_ospf6d_SOURCES = \
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_network.c b/ospfd/ospf_network.c
index 00fbdc21a1..be06afe532 100644
--- a/ospfd/ospf_network.c
+++ b/ospfd/ospf_network.c
@@ -190,7 +190,7 @@ int ospf_sock_init(struct ospf *ospf)
flog_err(EC_LIB_SOCKET,
"ospf_read_sock_init: socket: %s",
safe_strerror(errno));
- exit(1);
+ return -1;
}
#ifdef IP_HDRINCL
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..3819478cfc 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -286,7 +286,7 @@ DEFPY (ospf_router_id,
for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area))
if (area->full_nbrs) {
vty_out(vty,
- "For this router-id change to take effect, use “clear ip ospf process” command\n");
+ "For this router-id change to take effect, use \"clear ip ospf process\" command\n");
return CMD_SUCCESS;
}
@@ -319,7 +319,7 @@ DEFUN_HIDDEN (ospf_router_id_old,
for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area))
if (area->full_nbrs) {
vty_out(vty,
- "For this router-id change to take effect, use “clear ip ospf process” command\n");
+ "For this router-id change to take effect, use \"clear ip ospf process\" command\n");
return CMD_SUCCESS;
}
@@ -352,7 +352,7 @@ DEFPY (no_ospf_router_id,
for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area))
if (area->full_nbrs) {
vty_out(vty,
- "For this router-id change to take effect, use “clear ip ospf process” command\n");
+ "For this router-id change to take effect, use \"clear ip ospf process\" command\n");
return CMD_SUCCESS;
}
@@ -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/path_pcep_config.c b/pathd/path_pcep_config.c
index 0349618304..4c16b83948 100644
--- a/pathd/path_pcep_config.c
+++ b/pathd/path_pcep_config.c
@@ -332,6 +332,7 @@ int path_pcep_config_initiate_path(struct path *path)
candidate = srte_candidate_add(
policy, path->nbkey.preference,
SRTE_ORIGIN_PCEP, path->originator);
+ candidate->policy->srp_id = path->srp_id;
strlcpy(candidate->name, path->name,
sizeof(candidate->name));
SET_FLAG(candidate->flags, F_CANDIDATE_NEW);
@@ -387,6 +388,7 @@ int path_pcep_config_update_path(struct path *path)
if (!candidate)
return 0;
+ candidate->policy->srp_id = path->srp_id;
// first clean up old segment list if present
if (candidate->lsp->segment_list) {
SET_FLAG(candidate->lsp->segment_list->flags,
diff --git a/pathd/path_pcep_pcc.c b/pathd/path_pcep_pcc.c
index 81a338ac63..b72a536ef4 100644
--- a/pathd/path_pcep_pcc.c
+++ b/pathd/path_pcep_pcc.c
@@ -128,6 +128,8 @@ static struct req_entry *push_new_req(struct pcc_state *pcc_state,
struct path *path);
static void repush_req(struct pcc_state *pcc_state, struct req_entry *req);
static struct req_entry *pop_req(struct pcc_state *pcc_state, uint32_t reqid);
+static struct req_entry *pop_req_no_reqid(struct pcc_state *pcc_state,
+ uint32_t reqid);
static bool add_reqid_mapping(struct pcc_state *pcc_state, struct path *path);
static void remove_reqid_mapping(struct pcc_state *pcc_state,
struct path *path);
@@ -558,7 +560,6 @@ void pcep_pcc_send_report(struct ctrl_state *ctrl_state,
if (is_stable && (real_status != PCEP_LSP_OPERATIONAL_DOWN)) {
PCEP_DEBUG("(%s)%s Send report for candidate path (!DOWN) %s",
__func__, pcc_state->tag, path->name);
- path->srp_id = 0;
path->status = real_status;
send_report(pcc_state, path);
}
@@ -1340,7 +1341,11 @@ void handle_pcep_comp_reply(struct ctrl_state *ctrl_state,
struct path *path;
path = pcep_lib_parse_path(msg);
- req = pop_req(pcc_state, path->req_id);
+ if (path->no_path) {
+ req = pop_req_no_reqid(pcc_state, path->req_id);
+ } else {
+ req = pop_req(pcc_state, path->req_id);
+ }
if (req == NULL) {
/* TODO: check the rate of bad computation reply and close
* the connection if more that a given rate.
@@ -1372,6 +1377,9 @@ void handle_pcep_comp_reply(struct ctrl_state *ctrl_state,
if (path->no_path) {
PCEP_DEBUG("%s Computation for path %s did not find any result",
pcc_state->tag, path->name);
+ free_req_entry(req);
+ pcep_free_path(path);
+ return;
} else if (validate_incoming_path(pcc_state, path, err, sizeof(err))) {
/* Updating a dynamic path will automatically delegate it */
pcep_thread_update_path(ctrl_state, pcc_state->id, path);
@@ -1847,6 +1855,20 @@ struct req_entry *pop_req(struct pcc_state *pcc_state, uint32_t reqid)
return req;
}
+struct req_entry *pop_req_no_reqid(struct pcc_state *pcc_state, uint32_t reqid)
+{
+ struct path path = {.req_id = reqid};
+ struct req_entry key = {.path = &path};
+ struct req_entry *req;
+
+ req = RB_FIND(req_entry_head, &pcc_state->requests, &key);
+ if (req == NULL)
+ return NULL;
+ RB_REMOVE(req_entry_head, &pcc_state->requests, req);
+
+ return req;
+}
+
bool add_reqid_mapping(struct pcc_state *pcc_state, struct path *path)
{
struct req_map_data *mapping;
@@ -1883,6 +1905,33 @@ uint32_t lookup_reqid(struct pcc_state *pcc_state, struct path *path)
bool has_pending_req_for(struct pcc_state *pcc_state, struct path *path)
{
+ struct req_entry key = {.path = path};
+ struct req_entry *req;
+
+
+ PCEP_DEBUG_PATH("(%s) %s", format_path(path), __func__);
+ /* Looking for request without result */
+ if (path->no_path || !path->first_hop) {
+ PCEP_DEBUG_PATH("%s Path : no_path|!first_hop", __func__);
+ /* ...and already was handle */
+ req = RB_FIND(req_entry_head, &pcc_state->requests, &key);
+ if (!req) {
+ /* we must purge remaining reqid */
+ PCEP_DEBUG_PATH("%s Purge pending reqid: no_path(%s)",
+ __func__,
+ path->no_path ? "TRUE" : "FALSE");
+ if (lookup_reqid(pcc_state, path) != 0) {
+ PCEP_DEBUG_PATH("%s Purge pending reqid: DONE ",
+ __func__);
+ remove_reqid_mapping(pcc_state, path);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+
return lookup_reqid(pcc_state, path) != 0;
}
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/pbrd/pbr_map.h b/pbrd/pbr_map.h
index caeadb0644..694b915f48 100644
--- a/pbrd/pbr_map.h
+++ b/pbrd/pbr_map.h
@@ -85,6 +85,17 @@ struct pbr_map_sequence {
uint32_t ruleno;
/*
+ * src and dst ports
+ */
+ uint16_t src_prt;
+ uint16_t dst_prt;
+
+ /*
+ * The ip protocol we want to match on
+ */
+ uint8_t ip_proto;
+
+ /*
* Our policy Catchers
*/
struct prefix *src;
diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c
index 3d56fc3daa..730f965cd0 100644
--- a/pbrd/pbr_vty.c
+++ b/pbrd/pbr_vty.c
@@ -193,6 +193,76 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd,
return CMD_SUCCESS;
}
+DEFPY(pbr_map_match_ip_proto, pbr_map_match_ip_proto_cmd,
+ "[no] match ip-protocol [tcp|udp]$ip_proto",
+ NO_STR
+ "Match the rest of the command\n"
+ "Choose an ip-protocol\n"
+ "Match on tcp flows\n"
+ "Match on udp flows\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+ struct protoent *p;
+
+ if (!no) {
+ p = getprotobyname(ip_proto);
+ if (!p) {
+ vty_out(vty, "Unable to convert %s to proto id\n",
+ ip_proto);
+ return CMD_WARNING;
+ }
+
+ pbrms->ip_proto = p->p_proto;
+ } else
+ pbrms->ip_proto = 0;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_match_src_port, pbr_map_match_src_port_cmd,
+ "[no] match src-port (1-65535)$port",
+ NO_STR
+ "Match the rest of the command\n"
+ "Choose the source port to use\n"
+ "The Source Port\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!no) {
+ if (pbrms->src_prt == port)
+ return CMD_SUCCESS;
+ else
+ pbrms->src_prt = port;
+ } else
+ pbrms->src_prt = 0;
+
+ pbr_map_check(pbrms, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_match_dst_port, pbr_map_match_dst_port_cmd,
+ "[no] match dst-port (1-65535)$port",
+ NO_STR
+ "Match the rest of the command\n"
+ "Choose the destination port to use\n"
+ "The Destination Port\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!no) {
+ if (pbrms->dst_prt == port)
+ return CMD_SUCCESS;
+ else
+ pbrms->dst_prt = port;
+ } else
+ pbrms->dst_prt = 0;
+
+ pbr_map_check(pbrms, true);
+
+ return CMD_SUCCESS;
+}
+
DEFPY(pbr_map_match_dscp, pbr_map_match_dscp_cmd,
"[no] match dscp DSCP$dscp",
NO_STR
@@ -674,6 +744,13 @@ static void vty_show_pbrms(struct vty *vty,
pbrms->installed ? "yes" : "no",
pbrms->reason ? rbuf : "Valid");
+ if (pbrms->ip_proto) {
+ struct protoent *p;
+
+ p = getprotobynumber(pbrms->ip_proto);
+ vty_out(vty, " IP Protocol Match: %s\n", p->p_name);
+ }
+
if (pbrms->src)
vty_out(vty, " SRC Match: %pFX\n", pbrms->src);
if (pbrms->dst)
@@ -1079,6 +1156,18 @@ static int pbr_vty_map_config_write_sequence(struct vty *vty,
if (pbrms->dst)
vty_out(vty, " match dst-ip %pFX\n", pbrms->dst);
+ if (pbrms->src_prt)
+ vty_out(vty, " match src-port %u\n", pbrms->src_prt);
+ if (pbrms->dst_prt)
+ vty_out(vty, " match dst-port %u\n", pbrms->dst_prt);
+
+ if (pbrms->ip_proto) {
+ struct protoent *p;
+
+ p = getprotobynumber(pbrms->ip_proto);
+ vty_out(vty, " match ip-protocol %s\n", p->p_name);
+ }
+
if (pbrms->dsfield & PBR_DSFIELD_DSCP)
vty_out(vty, " match dscp %u\n",
(pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
@@ -1169,6 +1258,9 @@ void pbr_vty_init(void)
install_element(CONFIG_NODE, &pbr_set_table_range_cmd);
install_element(CONFIG_NODE, &no_pbr_set_table_range_cmd);
install_element(INTERFACE_NODE, &pbr_policy_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_match_ip_proto_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_match_src_port_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_match_dst_port_cmd);
install_element(PBRMAP_NODE, &pbr_map_match_src_cmd);
install_element(PBRMAP_NODE, &pbr_map_match_dst_cmd);
install_element(PBRMAP_NODE, &pbr_map_match_dscp_cmd);
diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c
index fc5303c9d8..28def509d5 100644
--- a/pbrd/pbr_zebra.c
+++ b/pbrd/pbr_zebra.c
@@ -534,10 +534,11 @@ static void pbr_encode_pbr_map_sequence(struct stream *s,
stream_putl(s, pbrms->seqno);
stream_putl(s, pbrms->ruleno);
stream_putl(s, pbrms->unique);
+ stream_putc(s, pbrms->ip_proto); /* The ip_proto */
pbr_encode_pbr_map_sequence_prefix(s, pbrms->src, family);
- stream_putw(s, 0); /* src port */
+ stream_putw(s, pbrms->src_prt);
pbr_encode_pbr_map_sequence_prefix(s, pbrms->dst, family);
- stream_putw(s, 0); /* dst port */
+ stream_putw(s, pbrms->dst_prt);
stream_putc(s, pbrms->dsfield);
stream_putl(s, pbrms->mark);
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..71b2d9187a 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 false;
+ }
}
+ 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 false;
+ }
+ }
+
+ 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_msdp.c b/pimd/pim_msdp.c
index 2a8f0c1216..da8916ddbf 100644
--- a/pimd/pim_msdp.c
+++ b/pimd/pim_msdp.c
@@ -1217,6 +1217,7 @@ void pim_msdp_mg_free(struct pim_instance *pim, struct pim_msdp_mg **mgp)
if ((*mgp)->mbr_list)
list_delete(&(*mgp)->mbr_list);
+ SLIST_REMOVE(&pim->msdp.mglist, (*mgp), pim_msdp_mg, mg_entry);
XFREE(MTYPE_PIM_MSDP_MG, (*mgp));
}
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/redhat/frr.spec.in b/redhat/frr.spec.in
index e24a84d1e4..fbe4c4a1f1 100644
--- a/redhat/frr.spec.in
+++ b/redhat/frr.spec.in
@@ -199,8 +199,6 @@ Requires: initscripts
BuildRequires: pam-devel
%endif
%if "%{initsystem}" == "systemd"
-BuildRequires: systemd
-BuildRequires: systemd-devel
Requires(post): systemd
Requires(preun): systemd
Requires(postun): systemd
@@ -396,9 +394,6 @@ routing state through standard SNMP MIBs.
--disable-bgp-vnc \
%endif
--enable-isisd \
-%if "%{initsystem}" == "systemd"
- --enable-systemd \
-%endif
--enable-rpki \
%if %{with_bfdd}
--enable-bfdd \
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/bgpd/test_aspath.c b/tests/bgpd/test_aspath.c
index aaf3fd2aa4..c2d39752ab 100644
--- a/tests/bgpd/test_aspath.c
+++ b/tests/bgpd/test_aspath.c
@@ -469,7 +469,10 @@ static struct aspath_tests {
0,
0,
{
- COMMON_ATTRS, BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, 10,
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 10,
},
COMMON_ATTR_SIZE + 3,
},
@@ -482,7 +485,10 @@ static struct aspath_tests {
-1,
0,
{
- COMMON_ATTRS, BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, 8,
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 8,
},
COMMON_ATTR_SIZE + 3,
},
@@ -495,7 +501,10 @@ static struct aspath_tests {
-1,
0,
{
- COMMON_ATTRS, BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, 12,
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 12,
},
COMMON_ATTR_SIZE + 3,
},
@@ -510,7 +519,8 @@ static struct aspath_tests {
{
COMMON_ATTRS,
BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
- BGP_ATTR_AS_PATH, 10,
+ BGP_ATTR_AS_PATH,
+ 10,
},
COMMON_ATTR_SIZE + 3,
},
@@ -525,7 +535,8 @@ static struct aspath_tests {
{
COMMON_ATTRS,
BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
- BGP_ATTR_AS4_PATH, 10,
+ BGP_ATTR_AS4_PATH,
+ 10,
},
COMMON_ATTR_SIZE + 3,
},
@@ -540,7 +551,8 @@ static struct aspath_tests {
{
COMMON_ATTRS,
BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
- BGP_ATTR_AS4_PATH, 10,
+ BGP_ATTR_AS4_PATH,
+ 10,
},
COMMON_ATTR_SIZE + 3,
},
@@ -553,7 +565,10 @@ static struct aspath_tests {
0,
PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV,
{
- COMMON_ATTRS, BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, 18,
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 18,
},
COMMON_ATTR_SIZE + 3,
},
@@ -566,7 +581,10 @@ static struct aspath_tests {
-1,
PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV,
{
- COMMON_ATTRS, BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, 16,
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 16,
},
COMMON_ATTR_SIZE + 3,
},
@@ -579,7 +597,10 @@ static struct aspath_tests {
-1,
PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV,
{
- COMMON_ATTRS, BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, 20,
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 20,
},
COMMON_ATTR_SIZE + 3,
},
@@ -592,7 +613,10 @@ static struct aspath_tests {
-1,
PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV,
{
- COMMON_ATTRS, BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, 22,
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 22,
},
COMMON_ATTR_SIZE + 3,
},
@@ -607,7 +631,8 @@ static struct aspath_tests {
{
COMMON_ATTRS,
BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
- BGP_ATTR_AS_PATH, 18,
+ BGP_ATTR_AS_PATH,
+ 18,
},
COMMON_ATTR_SIZE + 3,
},
@@ -622,7 +647,8 @@ static struct aspath_tests {
{
COMMON_ATTRS,
BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
- BGP_ATTR_AS4_PATH, 14,
+ BGP_ATTR_AS4_PATH,
+ 14,
},
COMMON_ATTR_SIZE + 3,
},
@@ -637,7 +663,8 @@ static struct aspath_tests {
{
COMMON_ATTRS,
BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
- BGP_ATTR_AS4_PATH, 14,
+ BGP_ATTR_AS4_PATH,
+ 14,
},
COMMON_ATTR_SIZE + 3,
&test_segments[0],
@@ -648,12 +675,13 @@ static struct aspath_tests {
&test_segments[28],
"8466 3 52737 0 4096",
AS4_DATA,
- -1,
+ -2,
PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV,
{
COMMON_ATTRS,
BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
- BGP_ATTR_AS4_PATH, 22,
+ BGP_ATTR_AS4_PATH,
+ 22,
},
COMMON_ATTR_SIZE + 3,
},
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-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py b/tests/topotests/bgp-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py
index fbce2809e0..6728f76004 100755
--- a/tests/topotests/bgp-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py
@@ -77,6 +77,9 @@ from lib.common_config import (
# Required to instantiate the topology builder class.
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
+
#Global variables
PES = ['PE1', 'PE2']
HOSTS = ['host1', 'host2']
diff --git a/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py b/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py
index 089b1acb1c..9f26978259 100644
--- a/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py
+++ b/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py
@@ -42,6 +42,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class BgpAggregateAddressTopo1(Topo):
def build(self, *_args, **_opts):
diff --git a/tests/topotests/bgp_aspath_zero/__init__.py b/tests/topotests/bgp_aspath_zero/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_aspath_zero/__init__.py
diff --git a/tests/topotests/bgp_aspath_zero/exabgp.env b/tests/topotests/bgp_aspath_zero/exabgp.env
new file mode 100644
index 0000000000..28e642360a
--- /dev/null
+++ b/tests/topotests/bgp_aspath_zero/exabgp.env
@@ -0,0 +1,53 @@
+[exabgp.api]
+encoder = text
+highres = false
+respawn = false
+socket = ''
+
+[exabgp.bgp]
+openwait = 60
+
+[exabgp.cache]
+attributes = true
+nexthops = true
+
+[exabgp.daemon]
+daemonize = true
+pid = '/var/run/exabgp/exabgp.pid'
+user = 'exabgp'
+##daemonize = false
+
+[exabgp.log]
+all = false
+configuration = true
+daemon = true
+destination = '/var/log/exabgp.log'
+enable = true
+level = INFO
+message = false
+network = true
+packets = false
+parser = false
+processes = true
+reactor = true
+rib = false
+routes = false
+short = false
+timers = false
+
+[exabgp.pdb]
+enable = false
+
+[exabgp.profile]
+enable = false
+file = ''
+
+[exabgp.reactor]
+speed = 1.0
+
+[exabgp.tcp]
+acl = false
+bind = ''
+delay = 0
+once = false
+port = 179
diff --git a/tests/topotests/bgp_aspath_zero/peer1/exabgp.cfg b/tests/topotests/bgp_aspath_zero/peer1/exabgp.cfg
new file mode 100644
index 0000000000..fe9ea01eca
--- /dev/null
+++ b/tests/topotests/bgp_aspath_zero/peer1/exabgp.cfg
@@ -0,0 +1,17 @@
+neighbor 10.0.0.1 {
+ router-id 10.0.0.2;
+ local-address 10.0.0.2;
+ local-as 65001;
+ peer-as 65534;
+
+ static {
+ route 192.168.100.101/32 {
+ next-hop 10.0.0.2;
+ }
+
+ route 192.168.100.102/32 {
+ as-path [65000 0 65001];
+ next-hop 10.0.0.2;
+ }
+ }
+}
diff --git a/tests/topotests/bgp_aspath_zero/r1/bgpd.conf b/tests/topotests/bgp_aspath_zero/r1/bgpd.conf
new file mode 100644
index 0000000000..002a5c78c0
--- /dev/null
+++ b/tests/topotests/bgp_aspath_zero/r1/bgpd.conf
@@ -0,0 +1,6 @@
+!
+router bgp 65534
+ no bgp ebgp-requires-policy
+ neighbor 10.0.0.2 remote-as external
+ neighbor 10.0.0.2 timers 3 10
+!
diff --git a/tests/topotests/bgp_aspath_zero/r1/zebra.conf b/tests/topotests/bgp_aspath_zero/r1/zebra.conf
new file mode 100644
index 0000000000..22a26ac610
--- /dev/null
+++ b/tests/topotests/bgp_aspath_zero/r1/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r1-eth0
+ ip address 10.0.0.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_aspath_zero/test_bgp_aspath_zero.py b/tests/topotests/bgp_aspath_zero/test_bgp_aspath_zero.py
new file mode 100644
index 0000000000..903ab12a13
--- /dev/null
+++ b/tests/topotests/bgp_aspath_zero/test_bgp_aspath_zero.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+Test if BGP UPDATE with AS-PATH attribute with value zero (0)
+is threated as withdrawal.
+"""
+
+import os
+import sys
+import json
+import time
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from mininet.topo import Topo
+
+pytestmark = [pytest.mark.bgpd]
+
+
+class BgpAggregatorAsnZero(Topo):
+ def build(self, *_args, **_opts):
+ tgen = get_topogen(self)
+
+ r1 = tgen.add_router("r1")
+ peer1 = tgen.add_exabgp_peer(
+ "peer1", ip="10.0.0.2", defaultRoute="via 10.0.0.1"
+ )
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(r1)
+ switch.add_link(peer1)
+
+
+def setup_module(mod):
+ tgen = Topogen(BgpAggregatorAsnZero, mod.__name__)
+ tgen.start_topology()
+
+ router = tgen.gears["r1"]
+ router.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf"))
+ router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r1/bgpd.conf"))
+ router.start()
+
+ peer = tgen.gears["peer1"]
+ peer.start(os.path.join(CWD, "peer1"), os.path.join(CWD, "exabgp.env"))
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_aggregator_zero():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_converge():
+ output = json.loads(
+ tgen.gears["r1"].vtysh_cmd("show ip bgp neighbor 10.0.0.2 json")
+ )
+ expected = {
+ "10.0.0.2": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "More than one prefix seen at r1, SHOULD be only one."
+
+ def _bgp_has_correct_routes_without_asn_0():
+ output = json.loads(tgen.gears["r1"].vtysh_cmd("show ip bgp json"))
+ expected = {"routes": {"192.168.100.101/32": [{"valid": True}]}}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_has_correct_routes_without_asn_0)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed listing 192.168.100.101/32, SHOULD be accepted."
+
+
+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/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py b/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py
index 374cce21f6..4753c49397 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,12 +1134,7 @@ 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)
+ result = verify_fib_routes(tgen, addr_type, dut, input_dict_r1)
assert result is not True, "Testcase {} : Failed \n"
"Expected behavior: routes should not present in fib \n"
"Error: {}".format(tc_name, result)
@@ -1156,14 +1151,7 @@ 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)
+ result = verify_fib_routes(tgen, addr_type, dut, input_dict_r1)
assert result is not True, "Testcase {} : Failed \n"
"Expected behavior: routes should not present in fib \n"
"Error: {}".format(tc_name, result)
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..6aadff1cfa 100644
--- a/tests/topotests/bgp_community_alias/test_bgp-community-alias.py
+++ b/tests/topotests/bgp_community_alias/test_bgp-community-alias.py
@@ -40,6 +40,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
@@ -84,39 +86,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_community_change_update/test_bgp_community_change_update.py b/tests/topotests/bgp_community_change_update/test_bgp_community_change_update.py
index 95e63c617e..9f449d7979 100644
--- a/tests/topotests/bgp_community_change_update/test_bgp_community_change_update.py
+++ b/tests/topotests/bgp_community_change_update/test_bgp_community_change_update.py
@@ -59,7 +59,7 @@ from mininet.topo import Topo
from lib.common_config import step
from time import sleep
-pytestmark = [pytest.mark.bgpd, pytest.mark.bgpd]
+pytestmark = [pytest.mark.bgpd]
class TemplateTopo(Topo):
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_conditional_advertisement/test_bgp_conditional_advertisement.py b/tests/topotests/bgp_conditional_advertisement/test_bgp_conditional_advertisement.py
index 0e31ab1995..44f54c7b51 100644
--- a/tests/topotests/bgp_conditional_advertisement/test_bgp_conditional_advertisement.py
+++ b/tests/topotests/bgp_conditional_advertisement/test_bgp_conditional_advertisement.py
@@ -139,6 +139,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class BgpConditionalAdvertisementTopo(Topo):
def build(self, *_args, **_opts):
diff --git a/tests/topotests/bgp_default_afi_safi/test_bgp-default-afi-safi.py b/tests/topotests/bgp_default_afi_safi/test_bgp-default-afi-safi.py
index 28117b7fe4..6ed7023044 100644
--- a/tests/topotests/bgp_default_afi_safi/test_bgp-default-afi-safi.py
+++ b/tests/topotests/bgp_default_afi_safi/test_bgp-default-afi-safi.py
@@ -47,6 +47,8 @@ from lib.topolog import logger
from mininet.topo import Topo
from lib.common_config import step
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
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..6fbdfbe78a 100644
--- a/tests/topotests/bgp_default_route/test_bgp_default-originate.py
+++ b/tests/topotests/bgp_default_route/test_bgp_default-originate.py
@@ -38,6 +38,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
@@ -79,10 +81,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 +93,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_default_route_route_map_match/test_bgp_default-originate_route-map_match.py b/tests/topotests/bgp_default_route_route_map_match/test_bgp_default-originate_route-map_match.py
index 089c9a964e..e7e3512b17 100644
--- a/tests/topotests/bgp_default_route_route_map_match/test_bgp_default-originate_route-map_match.py
+++ b/tests/topotests/bgp_default_route_route_map_match/test_bgp_default-originate_route-map_match.py
@@ -38,6 +38,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
diff --git a/tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py b/tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py
index 42a6b6edf6..5852ac268b 100644
--- a/tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py
+++ b/tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py
@@ -41,6 +41,8 @@ from lib.topolog import logger
from mininet.topo import Topo
from lib.common_config import step
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
diff --git a/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py b/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py
index 12d1d01bfb..e2fa89fccb 100644
--- a/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py
+++ b/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py
@@ -40,6 +40,9 @@ from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
+
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
tgen = get_topogen(self)
diff --git a/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py b/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py
index 2622c33f5b..be87dc61cf 100644
--- a/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py
+++ b/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py
@@ -38,6 +38,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
diff --git a/tests/topotests/bgp_distance_change/test_bgp_distance_change.py b/tests/topotests/bgp_distance_change/test_bgp_distance_change.py
index f338d52e70..bf26714087 100644
--- a/tests/topotests/bgp_distance_change/test_bgp_distance_change.py
+++ b/tests/topotests/bgp_distance_change/test_bgp_distance_change.py
@@ -49,6 +49,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
diff --git a/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/test_bgp-ebgp-common-subnet-nexthop-unchanged.py b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/test_bgp-ebgp-common-subnet-nexthop-unchanged.py
index 3b99065fe0..6db2697e75 100644
--- a/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/test_bgp-ebgp-common-subnet-nexthop-unchanged.py
+++ b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/test_bgp-ebgp-common-subnet-nexthop-unchanged.py
@@ -51,6 +51,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
diff --git a/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py
index fa155dd5fe..2731d37fb0 100644
--- a/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py
+++ b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py
@@ -57,6 +57,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
diff --git a/tests/topotests/bgp_evpn_mh/test_evpn_mh.py b/tests/topotests/bgp_evpn_mh/test_evpn_mh.py
index f389632b1e..2dcf70f14a 100644
--- a/tests/topotests/bgp_evpn_mh/test_evpn_mh.py
+++ b/tests/topotests/bgp_evpn_mh/test_evpn_mh.py
@@ -50,6 +50,9 @@ from lib.topolog import logger
# Required to instantiate the topology builder class.
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd, pytest.mark.pimd]
+
+
#####################################################
##
## Network Topology Definition
@@ -594,14 +597,23 @@ def test_evpn_ead_update():
def ping_anycast_gw(tgen):
- local_host = tgen.gears["hostd11"]
- remote_host = tgen.gears["hostd21"]
-
# ping the anycast gw from the local and remote hosts to populate
# the mac address on the PEs
- cmd_str = "arping -I torbond -c 1 45.0.0.1"
- local_host.run(cmd_str)
- remote_host.run(cmd_str)
+ script_path = os.path.abspath(os.path.join(CWD, "../lib/scapy_sendpkt.py"))
+ intf = "torbond"
+ ipaddr = "45.0.0.1"
+ ping_cmd = [
+ script_path,
+ "--imports=Ether,ARP",
+ "--interface=" + intf,
+ "'Ether(dst=\"ff:ff:ff:ff:ff:ff\")/ARP(pdst=\"{}\")'".format(ipaddr)
+ ]
+ for name in ("hostd11", "hostd21"):
+ host = tgen.net[name]
+ stdout = host.cmd(ping_cmd)
+ stdout = stdout.strip()
+ if stdout:
+ host.logger.debug("%s: arping on %s for %s returned: %s", name, intf, ipaddr, stdout)
def check_mac(dut, vni, mac, m_type, esi, intf, ping_gw=False, tgen=None):
diff --git a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py
index 36605d44f0..59024f7b71 100644
--- a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py
+++ b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py
@@ -46,6 +46,7 @@ from lib.topolog import logger
# Required to instantiate the topology builder class.
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
class BGPEVPNTopo(Topo):
"Test topology builder"
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/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py
index ee57b9c479..330ae5e437 100644
--- a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py
+++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py
@@ -139,6 +139,9 @@ from lib.common_config import (
required_linux_kernel_version,
)
+pytestmark = [pytest.mark.bgpd]
+
+
# Reading the data from JSON File for topology and configuration creation
jsonFile = "{}/bgp_gr_topojson_topo1.json".format(CWD)
try:
diff --git a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py
index b6f8bf4cd9..e7ce216042 100644
--- a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py
+++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py
@@ -138,6 +138,9 @@ from lib.common_config import (
required_linux_kernel_version,
)
+pytestmark = [pytest.mark.bgpd]
+
+
# Reading the data from JSON File for topology and configuration creation
jsonFile = "{}/bgp_gr_topojson_topo2.json".format(CWD)
try:
diff --git a/tests/topotests/bgp_gshut/test_bgp_gshut.py b/tests/topotests/bgp_gshut/test_bgp_gshut.py
index fe945a4565..77f86a0bb8 100644
--- a/tests/topotests/bgp_gshut/test_bgp_gshut.py
+++ b/tests/topotests/bgp_gshut/test_bgp_gshut.py
@@ -75,6 +75,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
diff --git a/tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py b/tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py
index 868aec9f3e..fcfeaab613 100644
--- a/tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py
+++ b/tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py
@@ -75,6 +75,9 @@ from lib.bgp import (
)
from lib.topojson import build_topo_from_json, build_config_from_json
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
# Reading the data from JSON File for topology and configuration creation
jsonFile = "{}/ebgp_gshut_topo1.json".format(CWD)
try:
diff --git a/tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py b/tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py
index 69f4916374..d83e9e25a1 100644
--- a/tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py
+++ b/tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py
@@ -75,6 +75,9 @@ from lib.bgp import (
)
from lib.topojson import build_topo_from_json, build_config_from_json
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
# Reading the data from JSON File for topology and configuration creation
jsonFile = "{}/ibgp_gshut_topo1.json".format(CWD)
try:
diff --git a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py
index 31fbdcd4b5..69eba23e0f 100644
--- a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py
+++ b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py
@@ -73,6 +73,9 @@ from lib.topolog import logger
from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp_and_verify
from lib.topojson import build_topo_from_json, build_config_from_json
+pytestmark = [pytest.mark.bgpd]
+
+
# Save the Current Working Directory to find configuration files.
CWD = os_path.dirname(os_path.realpath(__file__))
sys.path.append(os_path.join(CWD, "../"))
diff --git a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py
index 84d9c48f35..b033c7e5cd 100644
--- a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py
+++ b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py
@@ -97,6 +97,9 @@ from lib.topolog import logger
from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp_and_verify
from lib.topojson import build_topo_from_json, build_config_from_json
+pytestmark = [pytest.mark.bgpd]
+
+
# Reading the data from JSON File for topology and configuration creation
jsonFile = "{}/bgp_large_community_topo_2.json".format(CWD)
diff --git a/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py b/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py
index f09ff20651..3fcc3bec9a 100644
--- a/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py
+++ b/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py
@@ -46,6 +46,9 @@ from lib.topolog import logger
# Required to instantiate the topology builder class.
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
+
"""
This topology is for validating one of the primary use cases for
weighted ECMP (a.k.a. Unequal cost multipath) using BGP link-bandwidth:
diff --git a/tests/topotests/bgp_listen_on_multiple_addresses/test_bgp_listen_on_multiple_addresses.py b/tests/topotests/bgp_listen_on_multiple_addresses/test_bgp_listen_on_multiple_addresses.py
index a3ca1408e2..a7959fe61b 100755
--- a/tests/topotests/bgp_listen_on_multiple_addresses/test_bgp_listen_on_multiple_addresses.py
+++ b/tests/topotests/bgp_listen_on_multiple_addresses/test_bgp_listen_on_multiple_addresses.py
@@ -50,11 +50,14 @@ sys.path.append(os.path.join(CWD, "../"))
from lib.topogen import Topogen, get_topogen
from lib.topojson import build_topo_from_json, build_config_from_json
+from lib.topojson import linux_intf_config_from_json
from lib.common_config import start_topology
from lib.topotest import router_json_cmp, run_and_expect
from mininet.topo import Topo
from functools import partial
+pytestmark = [pytest.mark.bgpd]
+
LISTEN_ADDRESSES = {
"r1": ["10.0.0.1"],
@@ -94,6 +97,9 @@ def setup_module(mod):
)
start_topology(tgen)
+
+ linux_intf_config_from_json(tgen, topo)
+
build_config_from_json(tgen, topo)
diff --git a/tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py b/tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py
index 32e7a4df61..7c5ed87dd0 100644
--- a/tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py
+++ b/tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py
@@ -43,6 +43,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py b/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py
index 8494653dfe..0fde32a68b 100644
--- a/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py
+++ b/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py
@@ -47,6 +47,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
diff --git a/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py b/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py
index b99664e700..5c93910788 100644
--- a/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py
+++ b/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py
@@ -43,6 +43,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
diff --git a/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py b/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py
index 7ea5a24fd7..c9a93bd75f 100644
--- a/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py
+++ b/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py
@@ -145,6 +145,7 @@ from lib.bgp import (
)
from lib.topojson import build_topo_from_json, build_config_from_json
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
# Reading the data from JSON File for topology creation
jsonFile = "{}/bgp_multi_vrf_topo1.json".format(CWD)
diff --git a/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py b/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py
index d8815a0d39..01e90fb4b8 100644
--- a/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py
+++ b/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py
@@ -81,6 +81,10 @@ from functools import partial
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from lib import topotest
+
+pytestmark = [pytest.mark.bgpd]
+
+
fatal_error = ""
diff --git a/tests/topotests/bgp_path_attributes_topo1/test_bgp_path_attributes.py b/tests/topotests/bgp_path_attributes_topo1/test_bgp_path_attributes.py
index a9541a55c5..a591c2f3f4 100644
--- a/tests/topotests/bgp_path_attributes_topo1/test_bgp_path_attributes.py
+++ b/tests/topotests/bgp_path_attributes_topo1/test_bgp_path_attributes.py
@@ -94,6 +94,9 @@ from lib.bgp import (
)
from lib.topojson import build_topo_from_json, build_config_from_json
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
# Reading the data from JSON File for topology creation
jsonFile = "{}/bgp_path_attributes.json".format(CWD)
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py b/tests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py
index 39a0beeb11..743fcf7b3a 100755
--- a/tests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py
+++ b/tests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py
@@ -73,6 +73,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
class PeerTypeRelaxTopo(Topo):
def build(self, *_args, **_opts):
diff --git a/tests/topotests/bgp_prefix_list_topo1/test_prefix_lists.py b/tests/topotests/bgp_prefix_list_topo1/test_prefix_lists.py
index 22952f645c..10dee0f77b 100644
--- a/tests/topotests/bgp_prefix_list_topo1/test_prefix_lists.py
+++ b/tests/topotests/bgp_prefix_list_topo1/test_prefix_lists.py
@@ -73,6 +73,9 @@ from lib.topolog import logger
from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp_and_verify
from lib.topojson import build_topo_from_json, build_config_from_json
+pytestmark = [pytest.mark.bgpd]
+
+
# Reading the data from JSON File for topology creation
jsonFile = "{}/prefix_lists.json".format(CWD)
diff --git a/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py b/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py
index ceac84709b..fffe135b77 100644
--- a/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py
+++ b/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py
@@ -41,6 +41,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, **_opts):
diff --git a/tests/topotests/bgp_prefix_sid2/test_bgp_prefix_sid2.py b/tests/topotests/bgp_prefix_sid2/test_bgp_prefix_sid2.py
index 25362530d4..703dcd7e2d 100755
--- a/tests/topotests/bgp_prefix_sid2/test_bgp_prefix_sid2.py
+++ b/tests/topotests/bgp_prefix_sid2/test_bgp_prefix_sid2.py
@@ -41,6 +41,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, **_opts):
diff --git a/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py b/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py
index d514dccd4a..c644d2104f 100644
--- a/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py
+++ b/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py
@@ -50,6 +50,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
diff --git a/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py b/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py
index 0467bf1bfb..ecf1ed521c 100644
--- a/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py
+++ b/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py
@@ -70,6 +70,9 @@ from lib.bgp import (
)
from lib.topojson import build_topo_from_json, build_config_from_json
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
# Reading the data from JSON File for topology and configuration creation
jsonFile = "{}/bgp_aggregation.json".format(CWD)
try:
diff --git a/tests/topotests/bgp_route_map/test_route_map_topo1.py b/tests/topotests/bgp_route_map/test_route_map_topo1.py
index 74172501db..7de56849c8 100644
--- a/tests/topotests/bgp_route_map/test_route_map_topo1.py
+++ b/tests/topotests/bgp_route_map/test_route_map_topo1.py
@@ -67,6 +67,9 @@ from lib.bgp import (
)
from lib.topojson import build_topo_from_json, build_config_from_json
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
#################################
# TOPOLOGY
#################################
diff --git a/tests/topotests/bgp_route_map/test_route_map_topo2.py b/tests/topotests/bgp_route_map/test_route_map_topo2.py
index 958eceba62..230a89ace1 100644
--- a/tests/topotests/bgp_route_map/test_route_map_topo2.py
+++ b/tests/topotests/bgp_route_map/test_route_map_topo2.py
@@ -149,6 +149,9 @@ from lib.bgp import (
)
from lib.topojson import build_topo_from_json, build_config_from_json
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
# Reading the data from JSON File for topology and configuration creation
jsonFile = "{}/bgp_route_map_topo2.json".format(CWD)
diff --git a/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py b/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py
index 6a604765ca..664c9dc91a 100644
--- a/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py
+++ b/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py
@@ -49,6 +49,9 @@ from lib.topolog import logger
# Required to instantiate the topology builder class.
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
+
#####################################################
##
## Network Topology Definition
diff --git a/tests/topotests/bgp_set_local_preference_add_subtract/test_bgp_set_local-preference_add_subtract.py b/tests/topotests/bgp_set_local_preference_add_subtract/test_bgp_set_local-preference_add_subtract.py
index af64648951..b4af911d91 100644
--- a/tests/topotests/bgp_set_local_preference_add_subtract/test_bgp_set_local-preference_add_subtract.py
+++ b/tests/topotests/bgp_set_local_preference_add_subtract/test_bgp_set_local-preference_add_subtract.py
@@ -44,6 +44,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py
index 2d80c66b0b..3251484514 100755
--- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py
@@ -39,6 +39,8 @@ from lib.topolog import logger
from lib.common_config import required_linux_kernel_version
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class Topology(Topo):
"""
diff --git a/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py b/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py
index c75055c26f..476f6b556b 100644
--- a/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py
+++ b/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py
@@ -40,6 +40,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
diff --git a/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py b/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py
index 7500c3b3ad..cb1d28cc06 100644
--- a/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py
+++ b/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py
@@ -51,6 +51,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
diff --git a/tests/topotests/bgp_update_delay/test_bgp_update_delay.py b/tests/topotests/bgp_update_delay/test_bgp_update_delay.py
index 71bd58bf73..2972a25f38 100644
--- a/tests/topotests/bgp_update_delay/test_bgp_update_delay.py
+++ b/tests/topotests/bgp_update_delay/test_bgp_update_delay.py
@@ -73,6 +73,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py
index 83682fb36d..d6f1058a98 100644
--- a/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py
+++ b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py
@@ -81,6 +81,9 @@ from lib.bgp import (
)
from lib.topojson import build_topo_from_json, build_config_from_json
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
# Reading the data from JSON File for topology creation
jsonFile = "{}/bgp_vrf_dynamic_route_leak_topo1.json".format(CWD)
try:
diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py
index 6e7495d929..f701529b52 100644
--- a/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py
+++ b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py
@@ -78,6 +78,9 @@ from lib.bgp import (
)
from lib.topojson import build_topo_from_json, build_config_from_json
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
# Reading the data from JSON File for topology creation
jsonFile = "{}/bgp_vrf_dynamic_route_leak_topo2.json".format(CWD)
try:
diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py
index a17819f747..57ba87e887 100644
--- a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py
+++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py
@@ -47,6 +47,8 @@ from lib.common_config import required_linux_kernel_version
# Required to instantiate the topology builder class.
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class BGPIPV6RTADVVRFTopo(Topo):
"Test topology builder"
diff --git a/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py b/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py
index 30bb9595b7..9889e1cdd5 100644
--- a/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py
+++ b/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py
@@ -44,6 +44,9 @@ from lib.topolog import logger
# Required to instantiate the topology builder class.
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
+
total_ebgp_peers = 1
CustomizeVrfWithNetns = True
diff --git a/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py b/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py
index 71f64e9b70..fcec0c23af 100644
--- a/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py
+++ b/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py
@@ -41,6 +41,8 @@ from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class BGPVRFTopo(Topo):
def build(self, *_args, **_opts):
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_pim_1/test_evpn_pim_topo1.py b/tests/topotests/evpn_pim_1/test_evpn_pim_topo1.py
index 260a197aca..b1f5daef1e 100644
--- a/tests/topotests/evpn_pim_1/test_evpn_pim_topo1.py
+++ b/tests/topotests/evpn_pim_1/test_evpn_pim_topo1.py
@@ -49,6 +49,9 @@ from lib.topolog import logger
# Required to instantiate the topology builder class.
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd, pytest.mark.bgpd]
+
+
#####################################################
##
## Network Topology Definition
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/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py
index 46e21857c8..09d66baa79 100644
--- a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py
+++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py
@@ -85,6 +85,9 @@ from lib.bgp import (
)
from lib.topojson import build_topo_from_json, build_config_from_json
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
# Reading the data from JSON File for topology creation
jsonFile = "{}/evpn_type5_chaos_topo1.json".format(CWD)
try:
diff --git a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py
index 1a399ab32e..521f2335b4 100644
--- a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py
+++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py
@@ -91,6 +91,9 @@ from lib.bgp import (
)
from lib.topojson import build_topo_from_json, build_config_from_json
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
# Reading the data from JSON File for topology creation
jsonFile = "{}/evpn_type5_topo1.json".format(CWD)
try:
diff --git a/tests/topotests/isis_lsp_bits_topo1/test_isis_lsp_bits_topo1.py b/tests/topotests/isis_lsp_bits_topo1/test_isis_lsp_bits_topo1.py
index 27dc1073c6..70dcff035f 100755
--- a/tests/topotests/isis_lsp_bits_topo1/test_isis_lsp_bits_topo1.py
+++ b/tests/topotests/isis_lsp_bits_topo1/test_isis_lsp_bits_topo1.py
@@ -84,6 +84,9 @@ from lib.topolog import logger
# Required to instantiate the topology builder class.
from mininet.topo import Topo
+pytestmark = [pytest.mark.isisd]
+
+
# Global multi-dimensional dictionary containing all expected outputs
outputs = {}
diff --git a/tests/topotests/isis_rlfa_topo1/test_isis_rlfa_topo1.py b/tests/topotests/isis_rlfa_topo1/test_isis_rlfa_topo1.py
index 9ad41c5934..ded1a4cc22 100755
--- a/tests/topotests/isis_rlfa_topo1/test_isis_rlfa_topo1.py
+++ b/tests/topotests/isis_rlfa_topo1/test_isis_rlfa_topo1.py
@@ -82,7 +82,7 @@ from lib.topolog import logger
# Required to instantiate the topology builder class.
from mininet.topo import Topo
-pytestmark = [pytest.mark.isisd]
+pytestmark = [pytest.mark.isisd, pytest.mark.ldpd]
# Global multi-dimensional dictionary containing all expected outputs
outputs = {}
diff --git a/tests/topotests/isis_snmp/test_isis_snmp.py b/tests/topotests/isis_snmp/test_isis_snmp.py
index 04e043847d..2cd07299b0 100755
--- a/tests/topotests/isis_snmp/test_isis_snmp.py
+++ b/tests/topotests/isis_snmp/test_isis_snmp.py
@@ -82,6 +82,8 @@ from lib.snmptest import SnmpTester
# Required to instantiate the topology builder class.
from mininet.topo import Topo
+pytestmark = [pytest.mark.isisd, pytest.mark.ldpd, pytest.mark.snmp]
+
class TemplateTopo(Topo):
"Test topology builder"
diff --git a/tests/topotests/ldp_snmp/test_ldp_snmp_topo1.py b/tests/topotests/ldp_snmp/test_ldp_snmp_topo1.py
index f47d906157..8052316d73 100644
--- a/tests/topotests/ldp_snmp/test_ldp_snmp_topo1.py
+++ b/tests/topotests/ldp_snmp/test_ldp_snmp_topo1.py
@@ -81,6 +81,7 @@ from lib.snmptest import SnmpTester
# Required to instantiate the topology builder class.
from mininet.topo import Topo
+pytestmark = [pytest.mark.ldpd, pytest.mark.isisd, pytest.mark.snmp]
class TemplateTopo(Topo):
"Test topology builder"
diff --git a/tests/topotests/ldp_sync_isis_topo1/test_ldp_sync_isis_topo1.py b/tests/topotests/ldp_sync_isis_topo1/test_ldp_sync_isis_topo1.py
index 331e6fafd4..44b34c485f 100644
--- a/tests/topotests/ldp_sync_isis_topo1/test_ldp_sync_isis_topo1.py
+++ b/tests/topotests/ldp_sync_isis_topo1/test_ldp_sync_isis_topo1.py
@@ -80,6 +80,8 @@ from lib.topolog import logger
# Required to instantiate the topology builder class.
from mininet.topo import Topo
+pytestmark = [pytest.mark.isisd, pytest.mark.ldpd]
+
class TemplateTopo(Topo):
"Test topology builder"
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..40da7c8fbe 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))
@@ -1491,7 +1517,7 @@ def verify_ospf_database(tgen, topo, dut, input_dict, expected=True):
@retry(retry_timeout=20)
-def verify_ospf_summary(tgen, topo, dut, input_dict, expected=True):
+def verify_ospf_summary(tgen, topo, dut, input_dict, ospf=None, expected=True):
"""
This API is to verify ospf routes by running
show ip ospf interface command.
@@ -1502,7 +1528,6 @@ def verify_ospf_summary(tgen, topo, dut, input_dict, expected=True):
* `topo` : topology descriptions
* `dut`: device under test
* `input_dict` : Input dict data, required when configuring from testcase
- * `expected` : expected results from API, by-default True
Usage
-----
@@ -1522,18 +1547,30 @@ def verify_ospf_summary(tgen, topo, dut, input_dict, expected=True):
True or False (Error Message)
"""
- logger.debug("Entering lib API: verify_ospf_summary()")
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
result = False
router = dut
logger.info("Verifying OSPF summary on router %s:", router)
- if "ospf" not in topo["routers"][dut]:
- errormsg = "[DUT: {}] OSPF is not configured on the router.".format(router)
- return errormsg
-
rnode = tgen.routers()[dut]
- show_ospf_json = run_frr_cmd(rnode, "show ip ospf summary detail json", isjson=True)
+
+ if ospf:
+ if 'ospf6' not in topo['routers'][dut]:
+ errormsg = "[DUT: {}] OSPF6 is not configured on the router.".format(
+ router)
+ return errormsg
+
+ show_ospf_json = run_frr_cmd(rnode, "show ipv6 ospf summary detail json",
+ isjson=True)
+ else:
+ if 'ospf' not in topo['routers'][dut]:
+ errormsg = "[DUT: {}] OSPF is not configured on the router.".format(
+ router)
+ return errormsg
+
+ show_ospf_json = run_frr_cmd(rnode, "show ip ospf summary detail json",
+ isjson=True)
# Verifying output dictionary show_ospf_json is empty or not
if not bool(show_ospf_json):
@@ -1542,35 +1579,31 @@ def verify_ospf_summary(tgen, topo, dut, input_dict, expected=True):
# To find neighbor ip type
ospf_summary_data = input_dict
+
+ if ospf:
+ show_ospf_json = show_ospf_json['default']
+
for ospf_summ, summ_data in ospf_summary_data.items():
if ospf_summ not in show_ospf_json:
continue
- summary = ospf_summary_data[ospf_summ]["Summary address"]
+ summary = ospf_summary_data[ospf_summ]['Summary address']
+
if summary in show_ospf_json:
for summ in summ_data:
if summ_data[summ] == show_ospf_json[summary][summ]:
- logger.info(
- "[DUT: %s] OSPF summary %s:%s is %s",
- router,
- summary,
- summ,
- summ_data[summ],
- )
+ logger.info("[DUT: %s] OSPF summary %s:%s is %s",
+ router, summary, summ, summ_data[summ])
result = True
else:
- errormsg = (
- "[DUT: {}] OSPF summary {}:{} is %s, "
- "Expected is {}".format(
- router, summary, summ, show_ospf_json[summary][summ]
- )
- )
+ errormsg = ("[DUT: {}] OSPF summary {} : {} is {}, "
+ "Expected is {}".format(router, summary, summ,show_ospf_json[
+ summary][summ], summ_data[summ] ))
return errormsg
- logger.debug("Exiting API: verify_ospf_summary()")
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return result
-
@retry(retry_timeout=30)
def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None,
tag=None, metric=None, fib=None):
@@ -1627,31 +1660,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 +1695,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 +1703,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 +1716,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 +1743,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 +1762,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 +1924,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 +1943,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 +2043,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 +2058,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 +2307,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 +2328,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/scapy_sendpkt.py b/tests/topotests/lib/scapy_sendpkt.py
new file mode 100755
index 0000000000..0bb6a72092
--- /dev/null
+++ b/tests/topotests/lib/scapy_sendpkt.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+#
+# July 29 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2021, LabN Consulting, L.L.C. ("LabN")
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+import argparse
+import logging
+import re
+import sys
+
+from scapy.all import conf, srp
+
+conf.verb = 0
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-i", "--interface", help="interface to send packet on.")
+ parser.add_argument("-I", "--imports", help="scapy symbols to import")
+ parser.add_argument(
+ "-t", "--timeout", type=float, default=2.0, help="timeout for reply receipts"
+ )
+ parser.add_argument("pktdef", help="scapy packet definition to send")
+ args = parser.parse_args()
+
+ if args.imports:
+ i = args.imports.replace("\n", "").strip()
+ if not re.match("[a-zA-Z0-9_ \t,]", i):
+ logging.critical('Invalid imports specified: "%s"', i)
+ sys.exit(1)
+ exec("from scapy.all import " + i, globals(), locals())
+
+ ans, unans = srp(eval(args.pktdef), iface=args.interface, timeout=args.timeout)
+ if not ans:
+ sys.exit(2)
+ for pkt in ans:
+ print(pkt.answer.show(dump=True))
+
+
+if __name__ == "__main__":
+ main()
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/topojson.py b/tests/topotests/lib/topojson.py
index fcc6c19868..1ae482a265 100644
--- a/tests/topotests/lib/topojson.py
+++ b/tests/topotests/lib/topojson.py
@@ -293,6 +293,24 @@ def build_topo_from_json(tgen, topo):
)
+def linux_intf_config_from_json(tgen, topo):
+ """Configure interfaces from linux based on topo."""
+ routers = topo["routers"]
+ for rname in routers:
+ router = tgen.gears[rname]
+ links = routers[rname]["links"]
+ for rrname in links:
+ link = links[rrname]
+ if rrname == "lo":
+ lname = "lo"
+ else:
+ lname = link["interface"]
+ if "ipv4" in link:
+ router.run("ip addr add {} dev {}".format(link["ipv4"], lname))
+ if "ipv6" in link:
+ router.run("ip -6 addr add {} dev {}".format(link["ipv6"], lname))
+
+
def build_config_from_json(tgen, topo, save_bkup=True):
"""
Reads initial configuraiton from JSON for each router, builds
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/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py b/tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py
index cd398a5111..827dde69ec 100644
--- a/tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py
+++ b/tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py
@@ -113,6 +113,9 @@ from lib.pim import (
from lib.topolog import logger
from lib.topojson import build_topo_from_json, build_config_from_json
+pytestmark = [pytest.mark.pimd, pytest.mark.staticd]
+
+
# Reading the data from JSON File for topology creation
jsonFile = "{}/mcast_pim_bsmp_01.json".format(CWD)
try:
diff --git a/tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py b/tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py
index 60bd6de35d..98af4433ab 100644
--- a/tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py
+++ b/tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py
@@ -104,8 +104,7 @@ from lib.pim import (
from lib.topolog import logger
from lib.topojson import build_topo_from_json, build_config_from_json
-
-pytestmark = [pytest.mark.pimd]
+pytestmark = [pytest.mark.pimd, pytest.mark.staticd]
# Reading the data from JSON File for topology creation
diff --git a/tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py b/tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py
index b880e0e462..99a6e5bacf 100755
--- a/tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py
+++ b/tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py
@@ -107,6 +107,9 @@ from lib.pim import (
from lib.topolog import logger
from lib.topojson import build_topo_from_json, build_config_from_json
+pytestmark = [pytest.mark.pimd]
+
+
# Reading the data from JSON File for topology creation
jsonFile = "{}/multicast_pim_sm_topo1.json".format(CWD)
try:
diff --git a/tests/topotests/multicast_pim_sm_topo2/test_multicast_pim_sm_topo2.py b/tests/topotests/multicast_pim_sm_topo2/test_multicast_pim_sm_topo2.py
index ad3b77b843..f30902c1b2 100755
--- a/tests/topotests/multicast_pim_sm_topo2/test_multicast_pim_sm_topo2.py
+++ b/tests/topotests/multicast_pim_sm_topo2/test_multicast_pim_sm_topo2.py
@@ -102,6 +102,9 @@ from lib.pim import (
from lib.topolog import logger
from lib.topojson import build_topo_from_json, build_config_from_json
+pytestmark = [pytest.mark.pimd]
+
+
# Reading the data from JSON File for topology creation
jsonFile = "{}/multicast_pim_sm_topo2.json".format(CWD)
try:
diff --git a/tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp.py b/tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp.py
index d73e8dc9e8..736cb1659c 100755
--- a/tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp.py
+++ b/tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp.py
@@ -153,6 +153,9 @@ from lib.pim import (
clear_ip_mroute_verify,
)
+pytestmark = [pytest.mark.pimd, pytest.mark.staticd]
+
+
# Reading the data from JSON File for topology and configuration creation
jsonFile = "{}/multicast_pim_static_rp.json".format(CWD)
try:
diff --git a/tests/topotests/nhrp_topo/test_nhrp_topo.py b/tests/topotests/nhrp_topo/test_nhrp_topo.py
index 1687961f34..f59e3ae1b9 100644
--- a/tests/topotests/nhrp_topo/test_nhrp_topo.py
+++ b/tests/topotests/nhrp_topo/test_nhrp_topo.py
@@ -45,6 +45,8 @@ from lib.topolog import logger
# Required to instantiate the topology builder class.
from mininet.topo import Topo
+pytestmark = [pytest.mark.nhrpd]
+
class NHRPTopo(Topo):
"Test topology builder"
@@ -115,7 +117,7 @@ def setup_module(mod):
)
# Initialize all routers.
- logger.info('Launching BGP, NHRP')
+ logger.info('Launching NHRP')
for name in router_list:
router = tgen.gears[name]
router.start()
diff --git a/tests/topotests/ospf6_topo1/test_ospf6_topo1.py b/tests/topotests/ospf6_topo1/test_ospf6_topo1.py
index bbd18a57ff..8a6544734a 100644
--- a/tests/topotests/ospf6_topo1/test_ospf6_topo1.py
+++ b/tests/topotests/ospf6_topo1/test_ospf6_topo1.py
@@ -91,6 +91,9 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
import platform
+pytestmark = [pytest.mark.ospfd]
+
+
#####################################################
##
## Network Topology Definition
diff --git a/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py b/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py
index b158099d9a..61a80cc9ec 100755
--- a/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py
+++ b/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py
@@ -94,6 +94,9 @@ from lib.topolog import logger
from lib.topotest import iproute2_is_vrf_capable
from lib.common_config import required_linux_kernel_version
+pytestmark = [pytest.mark.ospfd]
+
+
#####################################################
##
## Network Topology Definition
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py
index 41960ac79f..e61a6b5905 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py
@@ -70,6 +70,9 @@ from lib.ospf import (
verify_ospf_summary,
)
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
# Global variables
topo = None
# Reading the data from JSON File for topology creation
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py
index 393eb19a53..db177360b4 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py
@@ -70,6 +70,9 @@ from lib.ospf import (
verify_ospf_summary,
)
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
# Global variables
topo = None
# Reading the data from JSON File for topology creation
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py
index a7f2893eab..bdba8fd8e4 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py
@@ -56,6 +56,9 @@ from lib.topojson import build_topo_from_json, build_config_from_json
from lib.ospf import verify_ospf_neighbor, config_ospf_interface, clear_ospf
from ipaddress import IPv4Address
+pytestmark = [pytest.mark.ospfd]
+
+
# Global variables
topo = None
# Reading the data from JSON File for topology creation
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py
index 49ecaac9f7..5c57f8be25 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py
@@ -68,6 +68,9 @@ from lib.ospf import (
redistribute_ospf,
)
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
topo = None
# Reading the data from JSON File for topology creation
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py
index 47c6c45e39..96f781c150 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py
@@ -70,6 +70,9 @@ from lib.ospf import (
)
from ipaddress import IPv4Address
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
# Global variables
topo = None
# Reading the data from JSON File for topology creation
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_lan.py b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py
index d9b90a132a..c89a663380 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_lan.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py
@@ -71,6 +71,9 @@ from lib.ospf import (
)
from ipaddress import IPv4Address
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
# Global variables
topo = None
# Reading the data from JSON File for topology creation
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py b/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py
index 3644bff3dc..0af83548b9 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py
@@ -63,6 +63,9 @@ sys.path.append(os.path.join(CWD, "../lib/"))
# pylint: disable=C0413
# Import topogen and topotest helpers
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
# Global variables
topo = None
# Reading the data from JSON File for topology creation
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py b/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py
index be18ba5a78..0172f589c5 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py
@@ -68,6 +68,9 @@ from lib.ospf import (
verify_ospf_database,
)
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
# Global variables
topo = None
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py b/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py
index 0848f6c94a..bc6c248ad2 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py
@@ -65,6 +65,9 @@ from lib.ospf import (
redistribute_ospf,
)
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
# Global variables
topo = None
# Reading the data from JSON File for topology creation
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py
index f17346d5b1..0e2fef4a22 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py
@@ -66,6 +66,9 @@ from lib.ospf import (
verify_ospf_interface,
)
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
# Global variables
topo = None
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py b/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py
index e94680d974..a595bc0491 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py
@@ -69,6 +69,7 @@ from lib.ospf import (
verify_ospf_database,
)
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
# Global variables
topo = None
diff --git a/tests/topotests/ospf_dual_stack/test_ospf_dual_stack.py b/tests/topotests/ospf_dual_stack/test_ospf_dual_stack.py
index 5e7802fa04..b5f535cd06 100644
--- a/tests/topotests/ospf_dual_stack/test_ospf_dual_stack.py
+++ b/tests/topotests/ospf_dual_stack/test_ospf_dual_stack.py
@@ -42,6 +42,9 @@ from lib.ospf import (
verify_ospf_database,
)
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
# Global variables
topo = None
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/ospf_suppress_fa/test_ospf_suppress_fa.py b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py
index 76e50beb5c..a22fbf458a 100644
--- a/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py
+++ b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py
@@ -50,6 +50,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
# Required to instantiate the topology builder class.
from mininet.topo import Topo
+pytestmark = [pytest.mark.ospfd]
+
class NetworkTopo(Topo):
"OSPF topology builder"
diff --git a/tests/topotests/ospf_tilfa_topo1/test_ospf_tilfa_topo1.py b/tests/topotests/ospf_tilfa_topo1/test_ospf_tilfa_topo1.py
index 489690471c..b3da6e2a1a 100644
--- a/tests/topotests/ospf_tilfa_topo1/test_ospf_tilfa_topo1.py
+++ b/tests/topotests/ospf_tilfa_topo1/test_ospf_tilfa_topo1.py
@@ -71,6 +71,8 @@ from lib.topolog import logger
# Required to instantiate the topology builder class.
from mininet.topo import Topo
+pytestmark = [pytest.mark.ospfd]
+
class TemplateTopo(Topo):
"Test topology builder"
diff --git a/tests/topotests/ospf_topo2/test_ospf_topo2.py b/tests/topotests/ospf_topo2/test_ospf_topo2.py
index 6451f5fb32..8b8d5d6e9f 100644
--- a/tests/topotests/ospf_topo2/test_ospf_topo2.py
+++ b/tests/topotests/ospf_topo2/test_ospf_topo2.py
@@ -46,6 +46,8 @@ from lib.topolog import logger
# Required to instantiate the topology builder class.
from mininet.topo import Topo
+pytestmark = [pytest.mark.ospfd]
+
class OSPFTopo(Topo):
"Test topology builder"
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_asbr_summary_topo1.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_asbr_summary_topo1.json
new file mode 100644
index 0000000000..74a0de489f
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_asbr_summary_topo1.json
@@ -0,0 +1,198 @@
+{
+ "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
+ }
+ },
+ "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"
+ }
+ },
+ "r3-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "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
+ }
+ },
+ "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": {},
+ "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"
+ }
+ },
+ "r0-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR0"
+ },
+ "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",
+ "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_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_asbr_summary_topo1.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py
new file mode 100644
index 0000000000..6a4b60fbed
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py
@@ -0,0 +1,1928 @@
+#!/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 Summarisation 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
+from time import sleep
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ kill_router_daemons,
+ write_test_footer,
+ reset_config_on_routers,
+ stop_router,
+ start_router,
+ verify_rib,
+ create_static_routes,
+ step,
+ start_router_daemons,
+ create_route_maps,
+ shutdown_bringup_interface,
+ create_prefix_lists,
+ create_route_maps,
+ create_interfaces_cfg,
+ 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,
+ clear_ospf,
+ verify_ospf6_rib,
+ create_router_ospf,
+ verify_ospf_summary,
+)
+
+
+# Global variables
+topo = None
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/ospfv3_asbr_summary_topo1.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": [
+ "2011:0:20::1/128",
+ "2011:0:20::2/128",
+ "2011:0:20::3/128",
+ "2011:0:20::4/128",
+ "2011:0:20::5/128",
+ ],
+}
+NETWORK_11 = {
+ "ipv4": ["11.0.20.6/32", "11.0.20.7/32"],
+ "ipv6": ["2011:0:20::6/128", "2011:0:20::7/128"],
+}
+
+NETWORK2 = {
+ "ipv4": [
+ "12.0.20.1/32",
+ "12.0.20.2/32",
+ "12.0.20.3/32",
+ "12.0.20.4/32",
+ "12.0.20.5/32",
+ ],
+ "ipv6": [
+ "2012:0:20::1/128",
+ "2012:0:20::2/128",
+ "2012:0:20::3/128",
+ "2012:0:20::4/128",
+ "2012:0:20::5/128",
+ ],
+}
+SUMMARY = {
+ "ipv4": ["11.0.0.0/8", "12.0.0.0/8", "11.0.0.0/24"],
+ "ipv6": ["2011::/32", "2012::/32", "2011::/64", "2011::/24"],
+}
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A0 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A0
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A0 +---+
+
+TESTCASES =
+1. OSPF summarisation functionality.
+2. OSPF summarisation with advertise and no advertise option
+3. OSPF summarisation with route map modification of metric type.
+4. OSPF CLI Show verify ospf ASBR summary config and show commands behaviours.
+5. OSPF summarisation Chaos.
+"""
+
+
+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)
+ # 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
+ )
+
+ 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.
+
+ Parameters
+ ----------
+ * `dut` : DUT on which configs have to be made.
+ * `config` : True or False, True by default for configure, set False for
+ unconfiguration.
+ """
+ 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
+
+ Parameters
+ ----------
+ * `dut` : DUT on which configs have to be made.
+ * `config` : True or False, True by default for configure, set False for
+ unconfiguration.
+ """
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "connected"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {
+ "redistribute": [{"redist_type": "connected", "delete": True}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase: Failed \n Error: {}".format(result)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+def test_ospfv3_type5_summary_tc42_p0(request):
+ """OSPF summarisation functionality."""
+ 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")
+ reset_config_on_routers(tgen)
+
+ protocol = 'ospf'
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0")
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"],
+ "next_hop": "blackhole"
+ },
+ {
+ "network": NETWORK2["ipv6"],
+ "next_hop": "blackhole"
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ dut = 'r0'
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = 'r1'
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_static_rtes, protocol=protocol)
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5"
+ " routes to one route. with aggregate timer as 6 sec")
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [{
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "32"
+ }],
+ "aggr_timer": 6
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1.")
+ input_dict_summary = {
+ "r0": {
+ "static_routes": [{"network": SUMMARY["ipv6"][0]}]
+ }
+ }
+ dut = 'r1'
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_summary, protocol=protocol)
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5
+ }
+ }
+ dut = 'r0'
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6')
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Verify that originally advertised routes are withdraw from there"
+ " peer.")
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"],
+ "next_hop": "blackhole"
+ }
+ ]
+ }
+ }
+ dut = 'r1'
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: "\
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol,
+ expected=False)
+ assert result is not True, "Testcase {} : Failed" \
+ "Error: Routes still present in RIB".format(tc_name)
+
+ step("Delete the configured summary")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [{
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "32",
+ "del_aggr_timer": True,
+ "delete": True
+ }]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
+ dut = 'r1'
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: "\
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_summary, protocol=protocol, expected=False)
+ assert result is not True, "Testcase {} : Failed" \
+ "Error: Summary Route still present in RIB".format(tc_name)
+
+ step("show ip ospf summary should not have any summary address.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5
+ }
+ }
+ dut = 'r0'
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6', expected=False)
+ assert result is not True, "Testcase {} : Failed" \
+ "Error: Summary still present in DB".format(tc_name)
+
+ dut = 'r1'
+ step("All 5 routes are advertised after deletion of configured summary.")
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_static_rtes, protocol=protocol)
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Routes is missing in RIB".format(tc_name)
+
+ step("configure the summary again and delete static routes .")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [{
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "32"
+ }]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5
+ }
+ }
+ dut = 'r0'
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6')
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Summary missing in OSPF DB".format(tc_name)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"],
+ "next_hop": "blackhole",
+ "delete": True
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ input_dict_summary = {
+ "r0": {
+ "static_routes": [{"network": SUMMARY["ipv6"][0]}]
+ }
+ }
+ step("Verify that summary route is withdrawn from R1.")
+
+ dut = 'r1'
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: "\
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_summary, protocol=protocol, expected=False)
+ assert result is not True, "Testcase {} : Failed" \
+ "Error: Routes still present in RIB".format(tc_name)
+
+ step("Add back static routes.")
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"],
+ "next_hop": "blackhole"
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary"
+ " address on R0 and only one route is sent to R1.")
+ dut = 'r1'
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: "\
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict_static_rtes,
+ protocol=protocol, expected=False)
+ assert result is not True, "Testcase {} : Failed" \
+ "Error: Routes still present in RIB".format(tc_name)
+
+ input_dict_summary = {
+ "r0": {
+ "static_routes": [{"network": SUMMARY["ipv6"][0]}]
+ }
+ }
+ dut = 'r1'
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_summary, protocol=protocol)
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show configure summaries.")
+
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5
+ }
+ }
+ dut = 'r0'
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6')
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Configure new static route which is matching configured summary.")
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK_11["ipv6"],
+ "next_hop": "blackhole"
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ # step("verify that summary lsa is not refreshed.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ step("Delete one of the static route.")
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK_11["ipv6"],
+ "next_hop": "blackhole",
+ "delete": True
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ # step("verify that summary lsa is not refreshed.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ # step("Verify that deleted static route is removed from ospf LSDB.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ step(
+ "Configure redistribute connected and configure ospf external"
+ " summary address to summarise the connected routes.")
+
+ dut = 'r0'
+ red_connected(dut)
+ clear_ospf(tgen, dut, ospf='ospf6')
+
+ ip = topo['routers']['r0']['links']['r3']['ipv6']
+
+ ip_net = str(ipaddress.ip_interface(u'{}'.format(ip)).network)
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [{
+ "prefix": ip_net.split('/')[0],
+ "mask": "8"
+ }]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured "
+ "summary address on R0 and only one route is sent to R1.")
+
+ input_dict_summary = {
+ "r0": {
+ "static_routes": [{"network": "fd00::/64"}]
+ }
+ }
+ dut = 'r1'
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_summary, protocol=protocol)
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Shut one of the interface")
+ intf = topo['routers']['r0']['links']['r3-link0']['interface']
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ # step("verify that summary lsa is not refreshed.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ # step("Verify that deleted connected route is removed from ospf LSDB.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ step("Un do shut the interface")
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ # step("verify that summary lsa is not refreshed.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ # step("Verify that deleted connected route is removed from ospf LSDB.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ step("Delete OSPF process.")
+ ospf_del = {
+ "r0": {
+ "ospf6": {
+ "delete": True
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_del)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ step("Reconfigure ospf process with summary")
+ reset_config_on_routers(tgen)
+
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"],
+ "next_hop": "blackhole"
+ },
+ {
+ "network": NETWORK2["ipv6"],
+ "next_hop": "blackhole"
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ dut = 'r0'
+ red_static(dut)
+ red_connected(dut)
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [{
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "32"
+ }]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 and only one route is sent to R1.")
+
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5
+ }
+ }
+ dut = 'r0'
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6')
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Summary missing in OSPF DB".format(tc_name)
+
+
+ input_dict_summary = {
+ "r0": {
+ "static_routes": [{"network": SUMMARY["ipv6"][0]}]
+ }
+ }
+
+ dut = 'r1'
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_summary, protocol=protocol)
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Routes is missing in RIB".format(tc_name)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [{
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "32"
+ }]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ # step("verify that summary lsa is not refreshed.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ step("Delete the redistribute command in ospf.")
+ dut = 'r0'
+ red_connected(dut, config=False)
+ red_static(dut, config=False)
+
+ step("Verify that summary route is withdrawn from the peer.")
+
+ dut = 'r1'
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: "\
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_summary, protocol=protocol, expected=False)
+ assert result is not True, "Testcase {} : Failed" \
+ "Error: Routes still present in RIB".format(tc_name)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [{
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "32",
+ "metric": "1234"
+ }]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_type5_summary_tc46_p0(request):
+ """OSPF summarisation with advertise and no advertise option"""
+ 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 OSPF on all the routers of the topology.")
+ reset_config_on_routers(tgen)
+
+ protocol = 'ospf'
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0")
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"],
+ "next_hop": "blackhole"
+ },
+ {
+ "network": NETWORK2["ipv6"],
+ "next_hop": "blackhole"
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ dut = 'r0'
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = 'r1'
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_static_rtes, protocol=protocol)
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5"
+ " routes to one route with no advertise option.")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [{
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "32",
+ "advertise": False
+ }]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary"
+ " address on R0 and summary route is not advertised to neighbor as"
+ " no advertise is configured..")
+
+ input_dict_summary = {
+ "r0": {
+ "static_routes": [{"network": SUMMARY["ipv6"][0]}]
+ }
+ }
+ dut = 'r1'
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: "\
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary,
+ protocol=protocol, expected=False)
+ assert result is not True, "Testcase {} : Failed" \
+ "Error: Routes still present in RIB".format(tc_name)
+
+ step(
+ "Verify that show ip ospf summary should show the "
+ "configured summaries.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "External route count": 5
+ }
+ }
+ dut = 'r0'
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6')
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Delete the configured summary")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [{
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "32",
+ "delete": True
+ }]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step("Summary has 5 sec delay timer, sleep 5 secs...")
+ sleep(5)
+
+ step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
+ dut = 'r1'
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: "\
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_summary, protocol=protocol, expected=False)
+ assert result is not True, "Testcase {} : Failed" \
+ "Error: Summary Route still present in RIB".format(tc_name)
+
+ step("show ip ospf summary should not have any summary address.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "External route count": 5
+ }
+ }
+ dut = 'r0'
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6', expected=False)
+ assert result is not True, "Testcase {} : Failed" \
+ "Error: Summary still present in DB".format(tc_name)
+
+ step("Reconfigure summary with no advertise.")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [{
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "32",
+ "advertise": False
+ }]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary"
+ " address on R0 and summary route is not advertised to neighbor as"
+ " no advertise is configured..")
+
+ input_dict_summary = {
+ "r0": {
+ "static_routes": [{"network": SUMMARY["ipv6"][0]}]
+ }
+ }
+ dut = 'r1'
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: "\
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary,
+ protocol=protocol, expected=False)
+ assert result is not True, "Testcase {} : Failed" \
+ "Error: Routes still present in RIB".format(tc_name)
+
+ step(
+ "Verify that show ip ospf summary should show the "
+ "configured summaries.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "External route count": 5
+ }
+ }
+ dut = 'r0'
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6')
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Change summary address from no advertise to advertise "
+ "(summary-address 10.0.0.0 255.255.0.0)")
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [{
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "32",
+ "advertise": False
+ }]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [{
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "32"
+ }]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1.")
+ input_dict_summary = {
+ "r0": {
+ "static_routes": [{"network": SUMMARY["ipv6"][0]}]
+ }
+ }
+ dut = 'r1'
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_summary, protocol=protocol)
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5
+ }
+ }
+ dut = 'r0'
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6')
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Verify that originally advertised routes are withdraw from there"
+ " peer.")
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"],
+ "next_hop": "blackhole"
+ }
+ ]
+ }
+ }
+ dut = 'r1'
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: "\
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol,
+ expected=False)
+ assert result is not True, "Testcase {} : Failed" \
+ "Error: Routes is present in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_type5_summary_tc48_p0(request):
+ """OSPF summarisation with route map modification of metric type."""
+ 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")
+ reset_config_on_routers(tgen)
+
+ protocol = 'ospf'
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0")
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"],
+ "next_hop": "blackhole"
+ },
+ {
+ "network": NETWORK2["ipv6"],
+ "next_hop": "blackhole"
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ dut = 'r0'
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = 'r1'
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_static_rtes, protocol=protocol)
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5"
+ " routes to one route.")
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [{
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "32"
+ }]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1.")
+ input_dict_summary = {
+ "r0": {
+ "static_routes": [{"network": SUMMARY["ipv6"][0]}]
+ }
+ }
+ dut = 'r1'
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_summary, protocol=protocol)
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5
+ }
+ }
+ dut = 'r0'
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6')
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Verify that originally advertised routes are withdraw from there"
+ " peer.")
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"],
+ "next_hop": "blackhole"
+ }
+ ]
+ }
+ }
+ dut = 'r1'
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: "\
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol,
+ expected=False)
+ assert result is not True, "Testcase {} : Failed" \
+ "Error: Routes still present in RIB".format(tc_name)
+
+ step(
+ "Configure route map and & rule to permit configured summary address,"
+ " redistribute static & connected routes with the route map.")
+ step("Configure prefixlist to permit the static routes, add to route map.")
+ # 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)
+
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [{
+ "action": "permit",
+ "seq_id": '1',
+ "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)
+
+ 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 external routes are summarised to configured"
+ "summary address on R0 and only one route is sent to R1. Verify that "
+ "show ip ospf summary should show the configure summaries.")
+
+ input_dict_summary = {
+ "r0": {
+ "static_routes": [{"network": SUMMARY["ipv6"][0]}]
+ }
+ }
+ dut = 'r1'
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_summary, protocol=protocol)
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Routes is missing in RIB".format(tc_name)
+
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5
+ }
+ }
+ dut = 'r0'
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6')
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Configure metric type as 1 in route map.")
+
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [{
+ "seq_id": '1',
+ "action": "permit",
+ "set":{
+ "metric-type": "type-1"
+ }
+ }]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with metric type 2.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5
+ }
+ }
+ dut = 'r0'
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6')
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Un configure metric type from route map.")
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [{
+ "action": "permit",
+ "seq_id": '1',
+ "set":{
+ "metric-type": "type-1",
+ "delete": True
+ }
+ }]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with metric type 2.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5
+ }
+ }
+ dut = 'r0'
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6')
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Change rule from permit to deny in prefix list.")
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {
+ "seqid": 10,
+ "network": "any",
+ "action": "deny"
+ }
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step(
+ "Verify that previously originated summary lsa "
+ "is withdrawn from the neighbor.")
+ input_dict_summary = {
+ "r0": {
+ "static_routes": [{"network": SUMMARY["ipv6"][0]}]
+ }
+ }
+ dut = 'r1'
+
+ step("summary route has delay of 5 secs, wait for 5 secs")
+
+ sleep(5)
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: "\
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_summary, protocol=protocol, expected=False)
+ assert result is not True, "Testcase {} : Failed" \
+ "Error: Routes still present in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_type5_summary_tc51_p2(request):
+ """OSPF CLI Show.
+
+ verify ospf ASBR summary config and show commands behaviours.
+ """
+ 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")
+ reset_config_on_routers(tgen)
+
+ step("Configure all the supported OSPF ASBR summary commands on DUT.")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [{
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "32",
+ "tag": 4294967295
+ },
+ {
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "16",
+ "advertise": True
+ },
+ {
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "24",
+ "advertise": False
+ },
+ {
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "24",
+ "advertise": False
+ },
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+
+ step("Configure and re configure all the commands 10 times in a loop.")
+
+ for itrate in range(0,10):
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [{
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "8",
+ "tag": 4294967295
+ },
+ {
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "16",
+ "advertise": True
+ },
+ {
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "24",
+ "advertise": False
+ },
+ {
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "24",
+ "advertise": False
+ },
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [{
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "8",
+ "tag": 4294967295,
+ "delete": True
+ },
+ {
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "16",
+ "advertise": True,
+ "delete": True
+ },
+ {
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "24",
+ "advertise": False,
+ "delete": True
+ },
+ {
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "24",
+ "advertise": False,
+ "delete": True
+ },
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step("Verify the show commands")
+
+ input_dict = {
+ SUMMARY["ipv6"][3]: {
+ "Summary address": SUMMARY["ipv6"][3],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 0
+ }
+ }
+ dut = 'r0'
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6')
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Summary missing in OSPF DB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_type5_summary_tc49_p2(request):
+ """OSPF summarisation Chaos."""
+ 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")
+ reset_config_on_routers(tgen)
+
+ protocol = 'ospf'
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0")
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"],
+ "next_hop": "blackhole"
+ },
+ {
+ "network": NETWORK2["ipv6"],
+ "next_hop": "blackhole"
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ dut = 'r0'
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = 'r1'
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_static_rtes, protocol=protocol)
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5"
+ " routes to one route.")
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [{
+ "prefix": SUMMARY["ipv6"][0].split('/')[0],
+ "mask": "32"
+ }]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1.")
+ input_dict_summary = {
+ "r0": {
+ "static_routes": [{"network": SUMMARY["ipv6"][0]}]
+ }
+ }
+ dut = 'r1'
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_summary, protocol=protocol)
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5
+ }
+ }
+ dut = 'r0'
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6')
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Verify that originally advertised routes are withdraw from there"
+ " peer.")
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"],
+ "next_hop": "blackhole"
+ }
+ ]
+ }
+ }
+ dut = 'r1'
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: "\
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol,
+ expected=False)
+ assert result is not True, "Testcase {} : Failed" \
+ "Error: Routes still present in RIB".format(tc_name)
+
+ step('Reload the FRR router')
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, 'r0')
+ start_router(tgen, 'r0')
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1.")
+ input_dict_summary = {
+ "r0": {
+ "static_routes": [{"network": SUMMARY["ipv6"][0]}]
+ }
+ }
+ dut = 'r1'
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_summary, protocol=protocol)
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5
+ }
+ }
+ dut = 'r0'
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6')
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Verify that originally advertised routes are withdraw from there"
+ " peer.")
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"],
+ "next_hop": "blackhole"
+ }
+ ]
+ }
+ }
+ dut = 'r1'
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: "\
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol,
+ expected=False)
+ assert result is not True, "Testcase {} : Failed" \
+ "Error: Routes still present in RIB".format(tc_name)
+
+ step("Kill OSPF6d daemon on R0.")
+ kill_router_daemons(tgen, "r0", ["ospf6d"])
+
+ step("Bring up OSPF6d daemon on R0.")
+ start_router_daemons(tgen, "r0", ["ospf6d"])
+
+ step("Verify OSPF neighbors are up after bringing back ospf6d in R0")
+ # 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 external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1.")
+ input_dict_summary = {
+ "r0": {
+ "static_routes": [{"network": SUMMARY["ipv6"][0]}]
+ }
+ }
+ dut = 'r1'
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_summary, protocol=protocol)
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5
+ }
+ }
+ dut = 'r0'
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6')
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Verify that originally advertised routes are withdraw from there"
+ " peer.")
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"],
+ "next_hop": "blackhole"
+ }
+ ]
+ }
+ }
+ dut = 'r1'
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: "\
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol,
+ expected=False)
+ assert result is not True, "Testcase {} : Failed" \
+ "Error: Routes still present in RIB".format(tc_name)
+
+ step("restart zebrad")
+ kill_router_daemons(tgen, "r0", ["zebra"])
+
+ step("Bring up zebra daemon on R0.")
+ start_router_daemons(tgen, "r0", ["zebra"])
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1.")
+ input_dict_summary = {
+ "r0": {
+ "static_routes": [{"network": SUMMARY["ipv6"][0]}]
+ }
+ }
+ dut = 'r1'
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut,
+ input_dict_summary, protocol=protocol)
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5
+ }
+ }
+ dut = 'r0'
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6')
+ assert result is True, "Testcase {} : Failed" \
+ "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Verify that originally advertised routes are withdraw from there"
+ " peer.")
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"],
+ "next_hop": "blackhole"
+ }
+ ]
+ }
+ }
+ dut = 'r1'
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: "\
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol,
+ expected=False)
+ assert result is not True, "Testcase {} : Failed" \
+ "Error: Routes still present in RIB".format(tc_name)
+
+ 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_ecmp.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py
new file mode 100644
index 0000000000..50c5144b3f
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py
@@ -0,0 +1,523 @@
+#!/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
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
+# 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..d8cf3bd02d
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py
@@ -0,0 +1,875 @@
+#!/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
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
+# 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..860f17ba67 100644
--- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py
@@ -74,6 +74,9 @@ from lib.ospf import (
from ipaddress import IPv6Address
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
# Global variables
topo = None
@@ -281,6 +284,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 +598,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..0c1c51c78a 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
@@ -72,6 +72,9 @@ from lib.ospf import (
from ipaddress import IPv6Address
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
# Global variables
topo = None
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..77917a0239
--- /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, pytest.mark.ospfd]
+
+
+#
+# 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..cb207cb810
--- /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.ospfd, 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/tests/topotests/route_scale/test_route_scale.py b/tests/topotests/route_scale/test_route_scale.py
index bbd6ef8d60..469ad42d64 100644
--- a/tests/topotests/route_scale/test_route_scale.py
+++ b/tests/topotests/route_scale/test_route_scale.py
@@ -48,6 +48,9 @@ from lib.common_config import shutdown_bringup_interface
# Required to instantiate the topology builder class.
from mininet.topo import Topo
+pytestmark = [pytest.mark.sharpd]
+
+
#####################################################
##
## Network Topology Definition
diff --git a/tests/topotests/simple_snmp_test/test_simple_snmp.py b/tests/topotests/simple_snmp_test/test_simple_snmp.py
index 5647e2b663..bdb44816b6 100755
--- a/tests/topotests/simple_snmp_test/test_simple_snmp.py
+++ b/tests/topotests/simple_snmp_test/test_simple_snmp.py
@@ -90,6 +90,16 @@ def setup_module(mod):
r1 = tgen.gears["r1"]
+ r1.run("ip addr add 192.168.12.12/24 dev r1-eth0")
+ r1.run("ip -6 addr add 2000:1:1:12::12/64 dev r1-eth0")
+ r1.run("ip addr add 192.168.13.13/24 dev r1-eth1")
+ r1.run("ip -6 addr add 2000:1:1:13::13/64 dev r1-eth1")
+ r1.run("ip addr add 192.168.14.14/24 dev r1-eth2")
+ r1.run("ip -6 addr add 2000:1:1:14::14/64 dev r1-eth2")
+ r1.run("ip addr add 1.1.1.1/32 dev lo")
+ r1.run("ip -6 addr add 2000:1:1:1::1/128 dev lo")
+ r1.run("ip addr show")
+
router_list = tgen.routers()
# For all registred routers, load the zebra configuration file
diff --git a/tests/topotests/srv6_locator/test_srv6_locator.py b/tests/topotests/srv6_locator/test_srv6_locator.py
index a7416ce085..04b0d8db97 100755
--- a/tests/topotests/srv6_locator/test_srv6_locator.py
+++ b/tests/topotests/srv6_locator/test_srv6_locator.py
@@ -43,6 +43,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd, pytest.mark.sharpd]
+
def open_json_file(filename):
try:
diff --git a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py
index 812b39797f..626de6b422 100644
--- a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py
+++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py
@@ -72,6 +72,9 @@ from lib.bgp import (
)
from lib.topojson import build_topo_from_json, build_config_from_json
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
# Reading the data from JSON File for topology creation
jsonFile = "{}/static_routes_topo4_ebgp.json".format(CWD)
try:
diff --git a/tests/topotests/zebra_netlink/test_zebra_netlink.py b/tests/topotests/zebra_netlink/test_zebra_netlink.py
index 94baf8438f..cf08ee9639 100644
--- a/tests/topotests/zebra_netlink/test_zebra_netlink.py
+++ b/tests/topotests/zebra_netlink/test_zebra_netlink.py
@@ -47,6 +47,9 @@ from lib.common_config import shutdown_bringup_interface
# Required to instantiate the topology builder class.
from mininet.topo import Topo
+pytestmark = [pytest.mark.sharpd]
+
+
#####################################################
##
## Network Topology Definition
diff --git a/tests/topotests/zebra_opaque/test_zebra_opaque.py b/tests/topotests/zebra_opaque/test_zebra_opaque.py
index cc52fbd1a7..2339b0f5b0 100644
--- a/tests/topotests/zebra_opaque/test_zebra_opaque.py
+++ b/tests/topotests/zebra_opaque/test_zebra_opaque.py
@@ -38,6 +38,8 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from mininet.topo import Topo
+pytestmark = [pytest.mark.bgpd]
+
class TemplateTopo(Topo):
def build(self, *_args, **_opts):
diff --git a/tests/topotests/zebra_rib/test_zebra_rib.py b/tests/topotests/zebra_rib/test_zebra_rib.py
index 9fcf7b6820..56d112b7c3 100644
--- a/tests/topotests/zebra_rib/test_zebra_rib.py
+++ b/tests/topotests/zebra_rib/test_zebra_rib.py
@@ -46,6 +46,8 @@ from time import sleep
# Required to instantiate the topology builder class.
from mininet.topo import Topo
+pytestmark = [pytest.mark.sharpd]
+
class ZebraTopo(Topo):
"Test topology builder"
@@ -124,7 +126,7 @@ def test_zebra_kernel_admin_distance():
"show ip route 4.5.{}.0 json".format(i),
expected,
)
- _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5)
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
assertmsg = '"r1" JSON output mismatches'
assert result is None, assertmsg
# tgen.mininet_cli()
@@ -145,7 +147,7 @@ def test_zebra_kernel_override():
test_func = partial(
topotest.router_json_cmp, r1, "show ip route 4.5.1.0 json", expected
)
- _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5)
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
assert result is None, '"r1" JSON output mismatches'
logger.info(
@@ -158,7 +160,7 @@ def test_zebra_kernel_override():
test_func = partial(
topotest.router_json_cmp, r1, "show ip route 4.5.1.0 json", expected
)
- _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5)
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
assert result is None, '"r1" JSON output mismatches'
diff --git a/tests/topotests/zebra_seg6_route/test_zebra_seg6_route.py b/tests/topotests/zebra_seg6_route/test_zebra_seg6_route.py
index e83b2c1007..a83c6d6ec0 100755
--- a/tests/topotests/zebra_seg6_route/test_zebra_seg6_route.py
+++ b/tests/topotests/zebra_seg6_route/test_zebra_seg6_route.py
@@ -43,6 +43,8 @@ from lib.topolog import logger
from lib.common_config import shutdown_bringup_interface
from mininet.topo import Topo
+pytestmark = [pytest.mark.sharpd]
+
def open_json_file(filename):
try:
diff --git a/tests/topotests/zebra_seg6local_route/test_zebra_seg6local_route.py b/tests/topotests/zebra_seg6local_route/test_zebra_seg6local_route.py
index 1c9d208fef..6cdb77b94b 100755
--- a/tests/topotests/zebra_seg6local_route/test_zebra_seg6local_route.py
+++ b/tests/topotests/zebra_seg6local_route/test_zebra_seg6local_route.py
@@ -43,6 +43,8 @@ from lib.topolog import logger
from lib.common_config import shutdown_bringup_interface
from mininet.topo import Topo
+pytestmark = [pytest.mark.sharpd]
+
def open_json_file(filename):
try:
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/coccinelle/xcalloc-xmalloc.cocci b/tools/coccinelle/xcalloc-xmalloc.cocci
new file mode 100644
index 0000000000..2a091d4cd9
--- /dev/null
+++ b/tools/coccinelle/xcalloc-xmalloc.cocci
@@ -0,0 +1,17 @@
+// No need checking against NULL for XMALLOC/XCALLOC.
+// If that happens, we have more problems with memory.
+
+@@
+type T;
+T *ptr;
+@@
+
+ptr =
+(
+XCALLOC(...)
+|
+XMALLOC(...)
+)
+...
+- if (ptr == 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/watchfrr/watchfrr.c b/watchfrr/watchfrr.c
index faf1777d7f..d0b4be81d4 100644
--- a/watchfrr/watchfrr.c
+++ b/watchfrr/watchfrr.c
@@ -469,12 +469,10 @@ static int run_job(struct restart_info *restart, const char *cmdtype,
return -1;
}
-#if defined HAVE_SYSTEMD
char buffer[512];
snprintf(buffer, sizeof(buffer), "restarting %s", restart->name);
systemd_send_status(buffer);
-#endif
/* Note: time_elapsed test must come before the force test, since we
need
@@ -506,9 +504,8 @@ static int run_job(struct restart_info *restart, const char *cmdtype,
restart->pid = 0;
}
-#if defined HAVE_SYSTEMD
systemd_send_status("FRR Operational");
-#endif
+
/* Calculate the new restart interval. */
if (update_interval) {
if (delay.tv_sec > 2 * gs.max_restart_interval)
@@ -718,10 +715,9 @@ static void daemon_send_ready(int exitcode)
fp = fopen(started, "w");
if (fp)
fclose(fp);
-#if defined HAVE_SYSTEMD
- systemd_send_started(master, 0);
+
+ systemd_send_started(master);
systemd_send_status("FRR Operational");
-#endif
sent = 1;
}
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_netlink.c b/zebra/kernel_netlink.c
index 8b631a3726..011883649d 100644
--- a/zebra/kernel_netlink.c
+++ b/zebra/kernel_netlink.c
@@ -550,6 +550,12 @@ bool nl_attr_put(struct nlmsghdr *n, unsigned int maxlen, int type,
return true;
}
+bool nl_attr_put8(struct nlmsghdr *n, unsigned int maxlen, int type,
+ uint8_t data)
+{
+ return nl_attr_put(n, maxlen, type, &data, sizeof(uint8_t));
+}
+
bool nl_attr_put16(struct nlmsghdr *n, unsigned int maxlen, int type,
uint16_t data)
{
diff --git a/zebra/kernel_netlink.h b/zebra/kernel_netlink.h
index a7b152b31b..d8e5671b72 100644
--- a/zebra/kernel_netlink.h
+++ b/zebra/kernel_netlink.h
@@ -38,6 +38,8 @@ extern "C" {
*/
extern bool nl_attr_put(struct nlmsghdr *n, unsigned int maxlen, int type,
const void *data, unsigned int alen);
+extern bool nl_attr_put8(struct nlmsghdr *n, unsigned int maxlen, int type,
+ uint8_t data);
extern bool nl_attr_put16(struct nlmsghdr *n, unsigned int maxlen, int type,
uint16_t data);
extern bool nl_attr_put32(struct nlmsghdr *n, unsigned int maxlen, int type,
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/rule_netlink.c b/zebra/rule_netlink.c
index 08a675ef3a..b651edd8f9 100644
--- a/zebra/rule_netlink.c
+++ b/zebra/rule_netlink.c
@@ -58,12 +58,11 @@
* Returns -1 on failure, 0 when the msg doesn't fit entirely in the buffer
* or the number of bytes written to buf.
*/
-static ssize_t
-netlink_rule_msg_encode(int cmd, const struct zebra_dplane_ctx *ctx,
- uint32_t filter_bm, uint32_t priority, uint32_t table,
- const struct prefix *src_ip,
- const struct prefix *dst_ip, uint32_t fwmark,
- uint8_t dsfield, void *buf, size_t buflen)
+static ssize_t netlink_rule_msg_encode(
+ int cmd, const struct zebra_dplane_ctx *ctx, uint32_t filter_bm,
+ uint32_t priority, uint32_t table, const struct prefix *src_ip,
+ const struct prefix *dst_ip, uint32_t fwmark, uint8_t dsfield,
+ uint8_t ip_protocol, void *buf, size_t buflen)
{
uint8_t protocol = RTPROT_ZEBRA;
int family;
@@ -136,6 +135,10 @@ netlink_rule_msg_encode(int cmd, const struct zebra_dplane_ctx *ctx,
if (filter_bm & PBR_FILTER_DSFIELD)
req->frh.tos = dsfield;
+ /* protocol to match on */
+ if (filter_bm & PBR_FILTER_IP_PROTOCOL)
+ nl_attr_put8(&req->n, buflen, FRA_IP_PROTO, ip_protocol);
+
/* Route table to use to forward, if filter criteria matches. */
if (table < 256)
req->frh.table = table;
@@ -168,7 +171,8 @@ static ssize_t netlink_rule_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf,
dplane_ctx_rule_get_table(ctx), dplane_ctx_rule_get_src_ip(ctx),
dplane_ctx_rule_get_dst_ip(ctx),
dplane_ctx_rule_get_fwmark(ctx),
- dplane_ctx_rule_get_dsfield(ctx), buf, buflen);
+ dplane_ctx_rule_get_dsfield(ctx),
+ dplane_ctx_rule_get_ipproto(ctx), buf, buflen);
}
static ssize_t netlink_oldrule_msg_encoder(struct zebra_dplane_ctx *ctx,
@@ -181,7 +185,8 @@ static ssize_t netlink_oldrule_msg_encoder(struct zebra_dplane_ctx *ctx,
dplane_ctx_rule_get_old_src_ip(ctx),
dplane_ctx_rule_get_old_dst_ip(ctx),
dplane_ctx_rule_get_old_fwmark(ctx),
- dplane_ctx_rule_get_old_dsfield(ctx), buf, buflen);
+ dplane_ctx_rule_get_old_dsfield(ctx),
+ dplane_ctx_rule_get_old_ipproto(ctx), buf, buflen);
}
/* Public functions */
@@ -236,6 +241,7 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
char *ifname;
struct zebra_pbr_rule rule = {};
uint8_t proto = 0;
+ uint8_t ip_proto = 0;
/* Basic validation followed by extracting attributes. */
if (h->nlmsg_type != RTM_NEWRULE && h->nlmsg_type != RTM_DELRULE)
@@ -312,6 +318,9 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
if (tb[FRA_PROTOCOL])
proto = *(uint8_t *)RTA_DATA(tb[FRA_PROTOCOL]);
+ if (tb[FRA_IP_PROTO])
+ ip_proto = *(uint8_t *)RTA_DATA(tb[FRA_IP_PROTO]);
+
ifname = (char *)RTA_DATA(tb[FRA_IFNAME]);
strlcpy(rule.ifname, ifname, sizeof(rule.ifname));
@@ -326,7 +335,7 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
ret = dplane_pbr_rule_delete(&rule);
zlog_debug(
- "%s: %s leftover rule: family %s IF %s Pref %u Src %pFX Dst %pFX Table %u",
+ "%s: %s leftover rule: family %s IF %s Pref %u Src %pFX Dst %pFX Table %u ip-proto: %u",
__func__,
((ret == ZEBRA_DPLANE_REQUEST_FAILURE)
? "Failed to remove"
@@ -334,7 +343,7 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
nl_family_to_str(frh->family), rule.ifname,
rule.rule.priority, &rule.rule.filter.src_ip,
&rule.rule.filter.dst_ip,
- rule.rule.action.table);
+ rule.rule.action.table, ip_proto);
}
/* TBD */
@@ -349,11 +358,12 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
- "Rx %s family %s IF %s Pref %u Src %pFX Dst %pFX Table %u",
+ "Rx %s family %s IF %s Pref %u Src %pFX Dst %pFX Table %u ip-proto: %u",
nl_msg_type_to_str(h->nlmsg_type),
nl_family_to_str(frh->family), rule.ifname,
rule.rule.priority, &rule.rule.filter.src_ip,
- &rule.rule.filter.dst_ip, rule.rule.action.table);
+ &rule.rule.filter.dst_ip, rule.rule.action.table,
+ ip_proto);
return kernel_pbr_rule_del(&rule);
}
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 8f2aa2fb09..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;
}
@@ -3131,6 +3131,7 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS)
STREAM_GETL(s, zpr.rule.seq);
STREAM_GETL(s, zpr.rule.priority);
STREAM_GETL(s, zpr.rule.unique);
+ STREAM_GETC(s, zpr.rule.filter.ip_proto);
STREAM_GETC(s, zpr.rule.filter.src_ip.family);
STREAM_GETC(s, zpr.rule.filter.src_ip.prefixlen);
STREAM_GET(&zpr.rule.filter.src_ip.u.prefix, s,
@@ -3164,6 +3165,9 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS)
if (zpr.rule.filter.dsfield)
zpr.rule.filter.filter_bm |= PBR_FILTER_DSFIELD;
+ if (zpr.rule.filter.ip_proto)
+ zpr.rule.filter.filter_bm |= PBR_FILTER_IP_PROTOCOL;
+
if (zpr.rule.filter.fwmark)
zpr.rule.filter.filter_bm |= PBR_FILTER_FWMARK;
@@ -3655,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 04411fa0d2..2a30fc6eef 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -259,6 +259,7 @@ struct dplane_ctx_rule {
uint8_t dsfield;
struct prefix src_ip;
struct prefix dst_ip;
+ uint8_t ip_proto;
char ifname[INTERFACE_NAMSIZ + 1];
};
@@ -1929,6 +1930,20 @@ uint32_t dplane_ctx_rule_get_old_fwmark(const struct zebra_dplane_ctx *ctx)
return ctx->u.rule.old.fwmark;
}
+uint8_t dplane_ctx_rule_get_ipproto(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.rule.new.ip_proto;
+}
+
+uint8_t dplane_ctx_rule_get_old_ipproto(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.rule.old.ip_proto;
+}
+
uint8_t dplane_ctx_rule_get_dsfield(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -2636,6 +2651,7 @@ static void dplane_ctx_rule_init_single(struct dplane_ctx_rule *dplane_rule,
dplane_rule->filter_bm = rule->rule.filter.filter_bm;
dplane_rule->fwmark = rule->rule.filter.fwmark;
dplane_rule->dsfield = rule->rule.filter.dsfield;
+ dplane_rule->ip_proto = rule->rule.filter.ip_proto;
prefix_copy(&(dplane_rule->dst_ip), &rule->rule.filter.dst_ip);
prefix_copy(&(dplane_rule->src_ip), &rule->rule.filter.src_ip);
strlcpy(dplane_rule->ifname, rule->ifname, INTERFACE_NAMSIZ);
@@ -3640,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_dplane.h b/zebra/zebra_dplane.h
index e091655a48..5ec1bd5807 100644
--- a/zebra/zebra_dplane.h
+++ b/zebra/zebra_dplane.h
@@ -493,6 +493,8 @@ uint32_t dplane_ctx_rule_get_fwmark(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_rule_get_old_fwmark(const struct zebra_dplane_ctx *ctx);
uint8_t dplane_ctx_rule_get_dsfield(const struct zebra_dplane_ctx *ctx);
uint8_t dplane_ctx_rule_get_old_dsfield(const struct zebra_dplane_ctx *ctx);
+uint8_t dplane_ctx_rule_get_ipproto(const struct zebra_dplane_ctx *ctx);
+uint8_t dplane_ctx_rule_get_old_ipproto(const struct zebra_dplane_ctx *ctx);
const struct prefix *
dplane_ctx_rule_get_src_ip(const struct zebra_dplane_ctx *ctx);
const struct prefix *
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_fpm.c b/zebra/zebra_fpm.c
index 07a8288605..855e19dc45 100644
--- a/zebra/zebra_fpm.c
+++ b/zebra/zebra_fpm.c
@@ -1002,7 +1002,6 @@ static int zfpm_build_route_updates(void)
data_len = zfpm_encode_route(dest, re, (char *)data,
buf_end - data, &msg_type);
- assert(data_len);
if (data_len) {
hdr->msg_type = msg_type;
msg_len = fpm_data_len_to_msg_len(data_len);
@@ -1013,6 +1012,9 @@ static int zfpm_build_route_updates(void)
zfpm_g->stats.route_adds++;
else
zfpm_g->stats.route_dels++;
+ } else {
+ zlog_err("%s: Encoding Prefix: %pRN No valid nexthops",
+ __func__, dest->rnode);
}
}
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_pbr.c b/zebra/zebra_pbr.c
index 7bcd097371..3607110aa2 100644
--- a/zebra/zebra_pbr.c
+++ b/zebra/zebra_pbr.c
@@ -166,10 +166,8 @@ uint32_t zebra_pbr_rules_hash_key(const void *arg)
rule->rule.action.table,
prefix_hash_key(&rule->rule.filter.src_ip));
- if (rule->rule.filter.fwmark)
- key = jhash_2words(rule->rule.filter.fwmark, rule->vrf_id, key);
- else
- key = jhash_1word(rule->vrf_id, key);
+ key = jhash_3words(rule->rule.filter.fwmark, rule->vrf_id,
+ rule->rule.filter.ip_proto, key);
key = jhash(rule->ifname, strlen(rule->ifname), key);
@@ -207,6 +205,9 @@ bool zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2)
if (r1->rule.filter.fwmark != r2->rule.filter.fwmark)
return false;
+ if (r1->rule.filter.ip_proto != r2->rule.filter.ip_proto)
+ return false;
+
if (!prefix_same(&r1->rule.filter.src_ip, &r2->rule.filter.src_ip))
return false;
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_vty.c b/zebra/zebra_vty.c
index 51f19a3c03..b204b30ca7 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -915,6 +915,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
json_object_string_add(json_route, "prefix",
srcdest_rnode2str(rn, buf, sizeof(buf)));
+ json_object_int_add(json_route, "prefixLen", rn->p.prefixlen);
json_object_string_add(json_route, "protocol",
zebra_route_string(re->type));
@@ -1121,8 +1122,10 @@ static void vty_show_ip_route_detail_json(struct vty *vty,
prefix2str(&rn->p, buf, sizeof(buf));
json_object_object_add(json, buf, json_prefix);
- vty_out(vty, "%s\n", json_object_to_json_string_ext(
- json, JSON_C_TO_STRING_PRETTY));
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY
+ | JSON_C_TO_STRING_NOSLASHESCAPE));
json_object_free(json);
}
@@ -1234,8 +1237,11 @@ static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf,
}
if (use_json) {
- vty_out(vty, "%s\n", json_object_to_json_string_ext(json,
- JSON_C_TO_STRING_PRETTY));
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json,
+ JSON_C_TO_STRING_PRETTY
+ | JSON_C_TO_STRING_NOSLASHESCAPE));
json_object_free(json);
}
}
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(